mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Adding SportModel, Warren Harris' partially complete garbage collector
code for the ElectricalFire JIT.
This commit is contained in:
parent
fc8d514fb4
commit
4a6bd744a3
35
ef/gc/Makefile
Normal file
35
ef/gc/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
#! gmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "NPL"); you may not use this file except in
|
||||
# compliance with the NPL. You may obtain a copy of the NPL at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
# for the specific language governing rights and limitations under the
|
||||
# NPL.
|
||||
#
|
||||
# The Initial Developer of this code under the NPL is Netscape
|
||||
# Communications Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
|
||||
DEPTH=..
|
||||
|
||||
DIRS=include src test
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
NSPR_HEADERS = $(wildcard $(DEPTH)/nspr20/pr/include/*.h)
|
||||
NSPR_PRIV = $(wildcard $(DEPTH)/nspr20/pr/include/private/*.h)
|
||||
NSPR_MD = $(wildcard $(DEPTH)/nspr20/pr/include/md/*.h)
|
||||
NSPR_OBS = $(wildcard $(DEPTH)/nspr20/pr/include/obsolete/*.h)
|
||||
|
||||
import:
|
||||
$(INSTALL) -m 444 $(NSPR_HEADERS) $(PUBLIC)/nspr20/
|
||||
$(INSTALL) -m 444 $(NSPR_PRIV) $(PUBLIC)/nspr20/private/
|
||||
$(INSTALL) -m 444 $(NSPR_MD) $(PUBLIC)/nspr20/md/
|
||||
$(INSTALL) -m 444 $(NSPR_OBS) $(PUBLIC)/nspr20/obsolete/
|
||||
$(INSTALL) -m 444 $(DIST)/lib/libnspr21.so $(DIST)/bin/libnspr21.so
|
||||
$(INSTALL) -m 444 $(DIST)/include/prcpucfg.h $(PUBLIC)/nspr20/
|
645
ef/gc/hash/dynahash.c
Normal file
645
ef/gc/hash/dynahash.c
Normal file
@ -0,0 +1,645 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Hash Tables: Based on Larson's in-memory adaption of linear hashing
|
||||
* [CACM, vol 31, #4, 1988 pp 446-457]
|
||||
******************************************************************************/
|
||||
|
||||
#include "dynahash.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#define MUL(x, y) ((x) << (y##_SHIFT))
|
||||
#define DIV(x, y) ((x) >> (y##_SHIFT))
|
||||
#define MOD(x, y) ((x) & ((y) - 1))
|
||||
|
||||
PR_IMPLEMENT(PLDynahash*)
|
||||
PL_NewDynahash(PRUint32 initialSize,
|
||||
PLHashFun hash, PLEqualFun equal,
|
||||
PLDestroyFun del, void* context)
|
||||
{
|
||||
PRStatus status;
|
||||
PLDynahash* self = (PLDynahash*)malloc(sizeof(PLDynahash));
|
||||
if (self == NULL)
|
||||
return self;
|
||||
status = PL_DynahashInit(self, initialSize, hash, equal, del, context);
|
||||
if (status != PR_SUCCESS) {
|
||||
PL_DynahashDestroy(self);
|
||||
return NULL;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_DynahashDestroy(PLDynahash* self)
|
||||
{
|
||||
PL_DynahashEmpty(self);
|
||||
free(self);
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PRStatus)
|
||||
PL_DynahashInit(PLDynahash* self, PRUint32 initialSize, PLHashFun hash,
|
||||
PLEqualFun equal, PLDestroyFun del, void* context)
|
||||
{
|
||||
PRUint32 count = DIV(initialSize, PL_DYNAHASH_BUCKETSIZE) + 1;
|
||||
self->directory = (PLHashDir*)count;
|
||||
self->hash = hash;
|
||||
self->equal = equal;
|
||||
self->destroy = del;
|
||||
self->keyCount = 0;
|
||||
self->nextBucketToSplit = 0;
|
||||
self->maxSize = (PRUint16)MUL(count, PL_DYNAHASH_BUCKETSIZE);
|
||||
self->bucketCount = 0;
|
||||
self->context = context;
|
||||
#ifdef PL_DYNAHASH_STATISTICS
|
||||
self->accesses = 0;
|
||||
self->collisions = 0;
|
||||
self->expansions = 0;
|
||||
#endif /* PL_DYNAHASH_STATISTICS */
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static PRStatus
|
||||
pl_DynahashAllocBucket(PLDynahash* self)
|
||||
{
|
||||
PLHashDir* newDir;
|
||||
PRUint32 i, size;
|
||||
PRUint16 count = 1;
|
||||
|
||||
if (self->bucketCount == 0) {
|
||||
count = (PRUint16)(PRInt32)self->directory;
|
||||
}
|
||||
|
||||
/* realloc directory */
|
||||
size = self->bucketCount + count;
|
||||
newDir = (PLHashDir*)calloc(sizeof(PLHashDir*), size);
|
||||
if (newDir == NULL)
|
||||
return PR_FAILURE;
|
||||
// memset((void*)newDir, 0, size * sizeof(PLHashBucket*));
|
||||
if (self->bucketCount) {
|
||||
memcpy((void*)newDir, self->directory, self->bucketCount
|
||||
* sizeof(PLHashBucket*));
|
||||
}
|
||||
|
||||
/* add new bucket(s) */
|
||||
for (i = self->bucketCount; i < size; i++) {
|
||||
PLHashBucket* newBucket;
|
||||
newBucket = (PLHashBucket*)calloc(sizeof(PLHashBucket*), PL_DYNAHASH_BUCKETSIZE);
|
||||
if (newBucket == NULL) return PR_FAILURE;
|
||||
// memset((void*)newBucket, 0, sizeof(PLDynahashElement*)
|
||||
// * PL_DYNAHASH_BUCKETSIZE);
|
||||
newDir[i] = newBucket;
|
||||
}
|
||||
|
||||
if (self->bucketCount != 0) {
|
||||
free(self->directory);
|
||||
}
|
||||
self->directory = newDir;
|
||||
self->bucketCount += count;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_DynahashCleanup(PLDynahash* self)
|
||||
{
|
||||
PL_DynahashEmpty(self);
|
||||
}
|
||||
|
||||
#define PL_DYNAHASH_HASH(resultVar, self, key) \
|
||||
{ \
|
||||
PRUword _h_ = self->hash(self->context, key); \
|
||||
PRUword _a_ = MOD(_h_, self->maxSize); \
|
||||
if (_a_ < self->nextBucketToSplit) { \
|
||||
_a_ = MOD(_h_, self->maxSize << 1); /* h % (2 * maxSize) */ \
|
||||
} \
|
||||
resultVar = _a_; \
|
||||
} \
|
||||
|
||||
PR_IMPLEMENT(PLDynahashElement*)
|
||||
PL_DynahashLookup(PLDynahash* self, PLDynahashElement* element)
|
||||
{
|
||||
PRUword h;
|
||||
PRUint32 bucketIndex, binIndex;
|
||||
PLHashBucket* bin;
|
||||
PLDynahashElement* result = NULL;
|
||||
PLHashDir* dir;
|
||||
PLDynahashElement* e;
|
||||
|
||||
if (self->bucketCount == 0) return NULL;
|
||||
|
||||
PL_DYNAHASH_HASH(h, self, element);
|
||||
bucketIndex = DIV(h, PL_DYNAHASH_BUCKETSIZE);
|
||||
binIndex = MOD(h, PL_DYNAHASH_BUCKETSIZE);
|
||||
|
||||
/* hash ensures valid segment */
|
||||
dir = self->directory;
|
||||
bin = dir[bucketIndex];
|
||||
PR_ASSERT(bin);
|
||||
|
||||
for (e = bin[binIndex]; e != NULL; e = e->next) {
|
||||
if (self->equal(self->context, e, element)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PRStatus
|
||||
pl_DynahashExpand(PLDynahash* self)
|
||||
{
|
||||
PRStatus err;
|
||||
if (self->maxSize + self->nextBucketToSplit
|
||||
< MUL(PL_DYNAHASH_MAX_DIR_SIZE, PL_DYNAHASH_BUCKETSIZE)) {
|
||||
PRUint32 oldDirIndex, oldBinIndex, newDirIndex, newBinIndex;
|
||||
PLHashBucket* oldSegment;
|
||||
PLHashBucket* newSegment;
|
||||
PRUword newAddress;
|
||||
PLDynahashElement** previous;
|
||||
PLDynahashElement* current;
|
||||
PLDynahashElement** lastOfNew;
|
||||
|
||||
/* locate the bucket to split */
|
||||
oldDirIndex = DIV(self->nextBucketToSplit, PL_DYNAHASH_BUCKETSIZE);
|
||||
oldSegment = self->directory[oldDirIndex];
|
||||
oldBinIndex = MOD(self->nextBucketToSplit, PL_DYNAHASH_BUCKETSIZE);
|
||||
|
||||
/* expand address space */
|
||||
newAddress = self->maxSize + self->nextBucketToSplit;
|
||||
newDirIndex = DIV(newAddress, PL_DYNAHASH_BUCKETSIZE);
|
||||
newBinIndex = MOD(newAddress, PL_DYNAHASH_BUCKETSIZE);
|
||||
|
||||
/* we need to unlock the directory handle here in case AllocBucket grows
|
||||
the directory */
|
||||
if (newBinIndex == 0) {
|
||||
err = pl_DynahashAllocBucket(self);
|
||||
if (err != PR_SUCCESS) return err;
|
||||
}
|
||||
PR_ASSERT(newDirIndex < self->bucketCount);
|
||||
|
||||
newSegment = self->directory[newDirIndex];
|
||||
|
||||
/* adjust state */
|
||||
self->nextBucketToSplit++;
|
||||
if (self->nextBucketToSplit == self->maxSize) {
|
||||
self->maxSize <<= 1; /* maxSize *= 2 */
|
||||
self->nextBucketToSplit = 0;
|
||||
}
|
||||
|
||||
/* relocate records to the new bucket */
|
||||
previous = &oldSegment[oldBinIndex];
|
||||
current = *previous;
|
||||
lastOfNew = &newSegment[newBinIndex];
|
||||
*lastOfNew = NULL;
|
||||
while (current != NULL) {
|
||||
PRUword hash;
|
||||
PL_DYNAHASH_HASH(hash, self, current);
|
||||
if (hash == newAddress) {
|
||||
/* attach to the end of the new chain */
|
||||
*lastOfNew = current;
|
||||
/* remove from old chain */
|
||||
*previous = current->next;
|
||||
lastOfNew = ¤t->next;
|
||||
current = current->next;
|
||||
*lastOfNew = NULL;
|
||||
}
|
||||
else {
|
||||
/* leave it on the old chain */
|
||||
previous = ¤t->next;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PRStatus)
|
||||
PL_DynahashAdd(PLDynahash* self, PLDynahashElement* element, PRBool replace,
|
||||
PLDynahashElement* *elementToDestroy)
|
||||
{
|
||||
/* Returns a pointer to the unused thing (either the key or the replaced key). */
|
||||
PRStatus err = PR_SUCCESS;
|
||||
PRUword h;
|
||||
PRUint32 bucketIndex, binIndex;
|
||||
PLHashBucket* b;
|
||||
PLDynahashElement** p;
|
||||
PLDynahashElement* q;
|
||||
PLDynahashElement* n;
|
||||
|
||||
*elementToDestroy = NULL;
|
||||
if (self->bucketCount == 0) {
|
||||
err = pl_DynahashAllocBucket(self);
|
||||
if (err != PR_SUCCESS) return err;
|
||||
}
|
||||
|
||||
PL_DYNAHASH_HASH(h, self, element);
|
||||
bucketIndex = DIV(h, PL_DYNAHASH_BUCKETSIZE);
|
||||
binIndex = MOD(h, PL_DYNAHASH_BUCKETSIZE);
|
||||
|
||||
/* hash ensures valid segment */
|
||||
b = self->directory[bucketIndex];
|
||||
PR_ASSERT(b);
|
||||
|
||||
/* follow collision chain */
|
||||
p = &b[binIndex];
|
||||
q = *p;
|
||||
|
||||
while (q && !self->equal(self->context, q, element)) {
|
||||
p = &q->next;
|
||||
q = *p;
|
||||
}
|
||||
if (q && !replace) {
|
||||
*elementToDestroy = element;
|
||||
goto done;
|
||||
}
|
||||
|
||||
n = element;
|
||||
*p = n;
|
||||
if (q) {
|
||||
/* replace q with n */
|
||||
n->next = q->next;
|
||||
}
|
||||
else {
|
||||
/* table over full? expand it: */
|
||||
if (++self->keyCount / MUL(self->bucketCount, PL_DYNAHASH_BUCKETSIZE)
|
||||
> PL_DYNAHASH_LOAD_FACTOR) {
|
||||
err = pl_DynahashExpand(self);
|
||||
if (err != PR_SUCCESS) return err;
|
||||
}
|
||||
}
|
||||
*elementToDestroy = q;
|
||||
done:;
|
||||
return err;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PLDynahashElement*)
|
||||
PL_DynahashRemove(PLDynahash* self, PLDynahashElement* element)
|
||||
{
|
||||
PRUword h;
|
||||
PRUint32 bucketIndex, binIndex;
|
||||
PLHashBucket* b;
|
||||
PLDynahashElement** p;
|
||||
PLDynahashElement* q;
|
||||
|
||||
if (self->bucketCount == 0)
|
||||
return NULL;
|
||||
|
||||
PL_DYNAHASH_HASH(h, self, element);
|
||||
bucketIndex = DIV(h, PL_DYNAHASH_BUCKETSIZE);
|
||||
binIndex = MOD(h, PL_DYNAHASH_BUCKETSIZE);
|
||||
b = self->directory[bucketIndex];
|
||||
PR_ASSERT(b);
|
||||
|
||||
p = &b[binIndex];
|
||||
q = *p;
|
||||
while (q && !self->equal(self->context, q, element)) {
|
||||
p = &q->next;
|
||||
q = *p;
|
||||
}
|
||||
if (q) {
|
||||
/* remove q */
|
||||
*p = q->next;
|
||||
--self->keyCount;
|
||||
/* XXX table can be shrunk? shrink it */
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
static PLDynahashElement*
|
||||
PL_DynahashRemove1(PLDynahash* self, PRUint32 bucketIndex, PRInt32 binIndex,
|
||||
int elemIndex)
|
||||
{
|
||||
/* only used by PLDynahashIterator, below */
|
||||
PLHashBucket* b;
|
||||
PLDynahashElement** p;
|
||||
PLDynahashElement* q;
|
||||
|
||||
if (self->bucketCount == 0)
|
||||
return NULL;
|
||||
|
||||
b = self->directory[bucketIndex];
|
||||
p = &b[binIndex];
|
||||
q = *p;
|
||||
|
||||
while (elemIndex--) {
|
||||
p = &q->next;
|
||||
q = *p;
|
||||
}
|
||||
if (q) {
|
||||
*p = q->next;
|
||||
--self->keyCount;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_DynahashEmpty(PLDynahash* self)
|
||||
{
|
||||
PRUint32 i, j;
|
||||
for (i = 0; i < self->bucketCount; i++) {
|
||||
PLHashBucket* b = self->directory[i];
|
||||
for (j = 0; j < PL_DYNAHASH_BUCKETSIZE; j++) {
|
||||
PLDynahashElement* q;
|
||||
PLDynahashElement* p = b[j];
|
||||
while (p) {
|
||||
q = p->next;
|
||||
self->destroy(self->context, p);
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
free(b);
|
||||
}
|
||||
if (self->bucketCount != 0) {
|
||||
free(self->directory);
|
||||
}
|
||||
self->keyCount = 0;
|
||||
self->nextBucketToSplit = 0;
|
||||
self->bucketCount = 0;
|
||||
self->directory = (PLHashDir*)1;
|
||||
self->maxSize = MUL(1, PL_DYNAHASH_BUCKETSIZE);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
PR_IMPLEMENT(PRBool)
|
||||
PL_DynahashIsValid(PLDynahash* self)
|
||||
{
|
||||
PLDynahashElement* hep;
|
||||
PLDynahashIterator iter;
|
||||
PL_DynahashIteratorInit(&iter, self);
|
||||
while ((hep = PL_DynahashIteratorNext(&iter)) != NULL) {
|
||||
if (PL_DynahashLookup(self, hep) != hep) {
|
||||
/* something else matches */
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*******************************************************************************
|
||||
* Hash Table Iterator
|
||||
******************************************************************************/
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_DynahashIteratorInit(PLDynahashIterator* self, PLDynahash* ht)
|
||||
{
|
||||
self->table = ht;
|
||||
self->bucket = 0;
|
||||
self->bin = -1;
|
||||
self->curno = 0;
|
||||
self->lbucket = 0;
|
||||
self->lbin = 0;
|
||||
self->lcurno = 0;
|
||||
self->nextp = NULL;
|
||||
self->curp = NULL;
|
||||
if (ht->bucketCount == 0) {
|
||||
self->done = PR_TRUE;
|
||||
self->b = NULL;
|
||||
}
|
||||
else {
|
||||
self->done = PR_FALSE;
|
||||
self->b = ht->directory[self->bucket];
|
||||
PL_DynahashIteratorNext(self); /* setup nextp */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pl_DynahashIteratorNext1(PLDynahashIterator* self)
|
||||
{
|
||||
if (!self->done) {
|
||||
/* save last position in case we have to reset */
|
||||
self->lbucket = self->bucket;
|
||||
self->lbin = self->bin;
|
||||
self->lcurno = self->curno;
|
||||
if (self->nextp) {
|
||||
self->nextp = self->nextp->next;
|
||||
self->curno++;
|
||||
}
|
||||
while (!self->nextp) {
|
||||
if (++self->bin >= PL_DYNAHASH_BUCKETSIZE) {
|
||||
self->bin = 0;
|
||||
if (++self->bucket < self->table->bucketCount) {
|
||||
self->b = self->table->directory[self->bucket];
|
||||
}
|
||||
else {
|
||||
self->done = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->nextp = self->b[self->bin];
|
||||
self->curno = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PLDynahashElement*)
|
||||
PL_DynahashIteratorNext(PLDynahashIterator* self)
|
||||
{
|
||||
/* This iterator precomputes one next element ahead so that we can detect
|
||||
when we dropped the current element and not skip elements. */
|
||||
if (self->curp != self->nextp) {
|
||||
self->done = PR_TRUE;
|
||||
}
|
||||
else {
|
||||
pl_DynahashIteratorNext1(self);
|
||||
}
|
||||
return self->curp;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PRUword)
|
||||
PL_DynahashIteratorCurrentAddress(PLDynahashIterator* self)
|
||||
{
|
||||
return (PRUword)(MUL(self->lbucket, PL_DYNAHASH_BUCKETSIZE) + self->lbin);
|
||||
}
|
||||
|
||||
static PLDynahashElement*
|
||||
PL_DynahashIteratorRemoveCurrent(PLDynahashIterator* self)
|
||||
{
|
||||
PLDynahashElement* dp = PL_DynahashRemove1(self->table,
|
||||
self->lbucket,
|
||||
self->lbin,
|
||||
(int) self->lcurno);
|
||||
if (dp != NULL) {
|
||||
if ((self->bin == self->lbin) && (self->bucket == self->lbucket)) {
|
||||
self->curno = self->lcurno;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_DynahashIteratorReset(PLDynahashIterator* self, PLDynahash* ht)
|
||||
{
|
||||
PRInt32 i;
|
||||
|
||||
self->table = ht;
|
||||
|
||||
if (self->done) return;
|
||||
|
||||
if (self->lbin == -1) {
|
||||
/* a single next has occurred, will emit first element twice */
|
||||
self->lbin = 0;
|
||||
}
|
||||
|
||||
/* recompute curp from lbucket/lbin/lcurno */
|
||||
if (self->lbucket >= self->table->bucketCount) {
|
||||
/* we're done */
|
||||
self->curp = self->nextp = 0;
|
||||
self->done = PR_TRUE;
|
||||
return;
|
||||
}
|
||||
self->b = self->table->directory[self->lbucket];
|
||||
self->curp = self->b[self->lbin];
|
||||
for (i = 0; i < self->lcurno && self->curp; i++) {
|
||||
self->curp = self->curp->next;
|
||||
}
|
||||
/* nextp also cannot be trusted */
|
||||
self->bucket = self->lbucket;
|
||||
self->bin = self->lbin;
|
||||
self->curno = self->lcurno;
|
||||
|
||||
if (self->curp == self->nextp) {
|
||||
/* special case where curp has been destroyed and nextp was the next one
|
||||
in the bin */
|
||||
return;
|
||||
}
|
||||
|
||||
/* curp mibht be zero, it doesn't matter */
|
||||
self->nextp = self->curp;
|
||||
pl_DynahashIteratorNext1(self);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Test
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
typedef struct MyHashElement {
|
||||
PLDynahashElement hashElement; /* must be first */
|
||||
PRUint32 key;
|
||||
PRUint32 value;
|
||||
} MyHashElement;
|
||||
|
||||
PRUint32
|
||||
Dynahash_Test_Hash(void* context, MyHashElement* x)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
return x->key;
|
||||
}
|
||||
|
||||
PRBool
|
||||
Dynahash_Test_equal(void* context, MyHashElement* x, MyHashElement* y)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
return (PRBool)(x->key == y->key);
|
||||
}
|
||||
|
||||
void
|
||||
Dynahash_Test_destroy(void* context, MyHashElement* x)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
free(x);
|
||||
}
|
||||
|
||||
#define kDynahash_Test_Iterations 1000
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
PR_IMPLEMENT(PRBool)
|
||||
PL_DynahashTest(void)
|
||||
{
|
||||
PRStatus err;
|
||||
PRUint32 i;
|
||||
PRBool result = PR_FALSE;
|
||||
PLDynahash* ht = PL_NewDynahash(14,
|
||||
(PLHashFun)Dynahash_Test_Hash,
|
||||
(PLEqualFun)Dynahash_Test_equal,
|
||||
(PLDestroyFun)Dynahash_Test_destroy,
|
||||
NULL);
|
||||
|
||||
for (i = 0; i < kDynahash_Test_Iterations; i++) {
|
||||
MyHashElement* oldElem;
|
||||
MyHashElement* elem;
|
||||
printf("seeding %ld\n", i);
|
||||
elem = (MyHashElement*)malloc(sizeof(MyHashElement));
|
||||
if (elem == NULL) goto done;
|
||||
elem->hashElement.next = NULL;
|
||||
elem->key = i;
|
||||
elem->value = i;
|
||||
err = PL_DynahashAdd(ht, (PLDynahashElement*)elem, PR_FALSE,
|
||||
(PLDynahashElement**)&oldElem);
|
||||
if (err != PR_SUCCESS) goto done;
|
||||
free(oldElem);
|
||||
}
|
||||
for (i = 0; i < kDynahash_Test_Iterations; i += 2) {
|
||||
MyHashElement elem;
|
||||
MyHashElement* found;
|
||||
PRBool wasFound = PR_FALSE;
|
||||
printf("deleting %ld\n", i);
|
||||
elem.hashElement.next = NULL;
|
||||
elem.key = i;
|
||||
found = (MyHashElement*)PL_DynahashRemove(ht, (PLDynahashElement*)&elem);
|
||||
if (found == NULL)
|
||||
goto done;
|
||||
wasFound = (PRBool)(found->value == i);
|
||||
if (!wasFound)
|
||||
goto done;
|
||||
}
|
||||
for (i = 0; i < kDynahash_Test_Iterations; i++) {
|
||||
MyHashElement elem;
|
||||
MyHashElement* found;
|
||||
printf("getting %ld\n", i);
|
||||
elem.hashElement.next = NULL;
|
||||
elem.key = i;
|
||||
found = (MyHashElement*)PL_DynahashLookup(ht, (PLDynahashElement*)&elem);
|
||||
if (found) {
|
||||
if (i & 1 == 1) {
|
||||
PRBool wasFound = (PRBool)(found->value == i);
|
||||
if (!wasFound) goto done;
|
||||
}
|
||||
else {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (i & 1 == 1) {
|
||||
goto done;
|
||||
}
|
||||
/* else ok */
|
||||
}
|
||||
}
|
||||
result = PR_TRUE;
|
||||
done:;
|
||||
PL_DynahashDestroy(ht);
|
||||
PR_ASSERT(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/******************************************************************************/
|
165
ef/gc/hash/dynahash.h
Normal file
165
ef/gc/hash/dynahash.h
Normal file
@ -0,0 +1,165 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Hash Tables: Based on Larson's in-memory adaption of linear hashing
|
||||
* [CACM, vol 31, #4, 1988 pp 446-457]
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef dynahash_h__
|
||||
#define dynahash_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*******************************************************************************
|
||||
* PLDynahash
|
||||
******************************************************************************/
|
||||
|
||||
#define PL_DYNAHASH_SIZE (1 << 16)
|
||||
#define PL_DYNAHASH_BUCKETSIZE_SHIFT 3
|
||||
#define PL_DYNAHASH_BUCKETSIZE (1 << PL_DYNAHASH_BUCKETSIZE_SHIFT)
|
||||
#define PL_DYNAHASH_MAX_DIR_SIZE (PL_DYNAHASH_SIZE / PL_DYNAHASH_BUCKETSIZE)
|
||||
#define PL_DYNAHASH_LOAD_FACTOR 2
|
||||
|
||||
#define PL_DYNAHASH_MAGIC_NUMBER 65521U
|
||||
|
||||
typedef struct PLDynahashElement {
|
||||
struct PLDynahashElement* next;
|
||||
/* other data follows */
|
||||
} PLDynahashElement;
|
||||
|
||||
typedef PLDynahashElement* PLHashBucket; /* array of PLDynahashElement handles */
|
||||
|
||||
typedef PLHashBucket* PLHashDir; /* array of HashBucket handles */
|
||||
|
||||
typedef PRUword
|
||||
(PR_CALLBACK* PLHashFun)(void* context, PLDynahashElement* x);
|
||||
|
||||
typedef PRBool
|
||||
(PR_CALLBACK* PLEqualFun)(void* context, PLDynahashElement* x, PLDynahashElement* y);
|
||||
|
||||
typedef void
|
||||
(PR_CALLBACK* PLDestroyFun)(void* context, PLDynahashElement* x);
|
||||
|
||||
typedef struct PLDynahash {
|
||||
PLHashDir* directory;
|
||||
PLHashFun hash;
|
||||
PLEqualFun equal;
|
||||
PLDestroyFun destroy;
|
||||
PRUint32 keyCount;
|
||||
PRUint16 nextBucketToSplit;
|
||||
PRUint16 maxSize;
|
||||
PRUint16 bucketCount;
|
||||
void* context;
|
||||
#ifdef PL_DYNAHASH_STATISTICS
|
||||
PRUint32 accesses;
|
||||
PRUint32 collisions;
|
||||
PRUint32 expansions;
|
||||
#endif /* PL_DYNAHASH_STATISTICS */
|
||||
} PLDynahash;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PR_EXTERN(PRStatus)
|
||||
PL_DynahashInit(PLDynahash* self, PRUint32 initialSize, PLHashFun hash,
|
||||
PLEqualFun equal, PLDestroyFun del, void* context);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_DynahashCleanup(PLDynahash* self);
|
||||
|
||||
PR_EXTERN(PLDynahash*)
|
||||
PL_NewDynahash(PRUint32 initialSize,
|
||||
PLHashFun hash, PLEqualFun equal,
|
||||
PLDestroyFun del, void* context);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_DynahashDestroy(PLDynahash* self);
|
||||
|
||||
PR_EXTERN(PLDynahashElement*)
|
||||
PL_DynahashLookup(PLDynahash* self, PLDynahashElement* element);
|
||||
|
||||
PR_EXTERN(PRStatus)
|
||||
PL_DynahashAdd(PLDynahash* self, PLDynahashElement* element, PRBool replace,
|
||||
PLDynahashElement* *elementToDestroy);
|
||||
|
||||
PR_EXTERN(PLDynahashElement*)
|
||||
PL_DynahashRemove(PLDynahash* self, PLDynahashElement* element);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_DynahashEmpty(PLDynahash* self);
|
||||
|
||||
#define PL_DynahashSize(self) ((self)->keyCount)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
PR_EXTERN(PRBool)
|
||||
PL_DynahashIsValid(PLDynahash* self);
|
||||
|
||||
PR_EXTERN(PRBool)
|
||||
PL_DynahashTest(void);
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
/*******************************************************************************
|
||||
* Hash Table Iterator
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct PLDynahashIterator {
|
||||
PLDynahash* table;
|
||||
PRBool done;
|
||||
PRUint32 bucket;
|
||||
PRInt32 bin;
|
||||
PRInt32 curno;
|
||||
PRUint32 lbucket;
|
||||
PRInt32 lbin;
|
||||
PRInt32 lcurno;
|
||||
PLHashBucket* b;
|
||||
PLDynahashElement* nextp;
|
||||
PLDynahashElement* curp;
|
||||
} PLDynahashIterator;
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_DynahashIteratorInit(PLDynahashIterator* self, PLDynahash* ht);
|
||||
|
||||
PR_EXTERN(PLDynahashElement*)
|
||||
PL_DynahashIteratorNext(PLDynahashIterator* self);
|
||||
|
||||
#define PL_CurrentDynahashIterator(self) ((self)->curp)
|
||||
|
||||
PR_EXTERN(PRUword)
|
||||
PL_DynahashIteratorCurrentAddress(PLDynahashIterator* self);
|
||||
|
||||
PR_EXTERN(PLDynahashElement*)
|
||||
PL_DynahashIteratorRemoveCurrent(PLDynahashIterator* self);
|
||||
|
||||
#define PL_DynahashIteratorIsDone(self) ((self)->done)
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_DynahashIteratorReset(PLDynahashIterator* self, PLDynahash* ht);
|
||||
|
||||
/******************************************************************************/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* dynahash_h__ */
|
||||
/******************************************************************************/
|
128
ef/gc/hash/openhash.c
Normal file
128
ef/gc/hash/openhash.c
Normal file
@ -0,0 +1,128 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "openhash.h"
|
||||
#include "prlog.h"
|
||||
|
||||
PR_IMPLEMENT(PRStatus)
|
||||
PL_OpenhashInit(PLOpenhash* self, PRUint32 initialSize,
|
||||
PLOpenhashHashFun hash, PLOpenhashEqualFun equal,
|
||||
PLOpenhashDestroyFun del, void* context)
|
||||
{
|
||||
self->header.size = initialSize;
|
||||
self->header.hash = hash;
|
||||
self->header.equal = equal;
|
||||
self->header.destroy = del;
|
||||
self->header.context = context;
|
||||
memset(self->element, 0, sizeof(PLOpenhashElement) * initialSize);
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_OpenhashCleanup(PLOpenhash* self)
|
||||
{
|
||||
PL_OpenhashEmpty(self);
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PLOpenhash*)
|
||||
PL_NewOpenhash(PRUint32 initialSize,
|
||||
PLOpenhashHashFun hash, PLOpenhashEqualFun equal,
|
||||
PLOpenhashDestroyFun del, void* context)
|
||||
{
|
||||
PRStatus status;
|
||||
PLOpenhash* self = malloc(sizeof(PLOpenhashHeader)
|
||||
+ initialSize * sizeof(PLOpenhashElement));
|
||||
if (self == NULL) return NULL;
|
||||
status = PL_OpenhashInit(self, initialSize, hash, equal, del, context);
|
||||
if (status != PR_SUCCESS) {
|
||||
PL_OpenhashDestroy(self);
|
||||
return NULL;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_OpenhashDestroy(PLOpenhash* self)
|
||||
{
|
||||
PL_OpenhashCleanup(self);
|
||||
free((void*)self);
|
||||
}
|
||||
|
||||
#define PR_GOLDEN_RATIO 616161
|
||||
#define PR_HASH1(key, size) (((key) * PR_GOLDEN_RATIO) & ((size) - 1))
|
||||
#define PR_HASH2(key, size) (((key) * 97) + 1)
|
||||
|
||||
PR_IMPLEMENT(void*)
|
||||
PL_OpenhashLookup(PLOpenhash* self, PRUword key)
|
||||
{
|
||||
PRUword htSize = self->header.size;
|
||||
PRUword hash1 = PR_HASH1(++key, htSize);
|
||||
PRUword hash2 = PR_HASH2(key, htSize);
|
||||
PRUword i = hash1;
|
||||
PLOpenhashElement* elements = self->element;
|
||||
|
||||
while (elements[i].key != 0) {
|
||||
if (elements[i].key == key)
|
||||
return elements[i].value;
|
||||
else {
|
||||
i = (i + hash2) % htSize;
|
||||
PR_ASSERT(i != hash1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_OpenhashAdd(PLOpenhash* self, PRUword key, void* value)
|
||||
{
|
||||
PRUword htSize = self->header.size;
|
||||
PRUword hash1 = PR_HASH1(++key, htSize);
|
||||
PRUword hash2 = PR_HASH2(key, htSize);
|
||||
PRUword i = hash1;
|
||||
PLOpenhashElement* elements = self->element;
|
||||
|
||||
while (elements[i].key != 0) {
|
||||
i = (i + hash2) % htSize;
|
||||
PR_ASSERT(i != hash1);
|
||||
}
|
||||
elements[i].key = key;
|
||||
elements[i].value = value;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PRStatus)
|
||||
PL_OpenhashRemove(PLOpenhash* self, PRUword key)
|
||||
{
|
||||
PR_ASSERT(0);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_OpenhashEmpty(PLOpenhash* self)
|
||||
{
|
||||
PRUword i;
|
||||
PLOpenhashElement* element = self->element;
|
||||
for (i = 0; i < self->header.size; i++) {
|
||||
if (element->key != 0) {
|
||||
self->header.destroy(self->header.context, element);
|
||||
element->key = 0;
|
||||
}
|
||||
element++;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
101
ef/gc/hash/openhash.h
Normal file
101
ef/gc/hash/openhash.h
Normal file
@ -0,0 +1,101 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef openhash_h__
|
||||
#define openhash_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*******************************************************************************
|
||||
* PLOpenhash
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct PLOpenhashElement {
|
||||
PRUword key;
|
||||
void* value;
|
||||
} PLOpenhashElement;
|
||||
|
||||
typedef PRUword
|
||||
(PR_CALLBACK* PLOpenhashHashFun)(void* context,
|
||||
PLOpenhashElement* x);
|
||||
|
||||
typedef PRBool
|
||||
(PR_CALLBACK* PLOpenhashEqualFun)(void* context,
|
||||
PLOpenhashElement* x, PLOpenhashElement* y);
|
||||
|
||||
typedef void
|
||||
(PR_CALLBACK* PLOpenhashDestroyFun)(void* context,
|
||||
PLOpenhashElement* x);
|
||||
|
||||
typedef struct PLOpenhashHeader {
|
||||
PRUword size;
|
||||
PLOpenhashHashFun hash;
|
||||
PLOpenhashEqualFun equal;
|
||||
PLOpenhashDestroyFun destroy;
|
||||
void* context;
|
||||
} PLOpenhashHeader;
|
||||
|
||||
typedef struct PLOpenhash {
|
||||
PLOpenhashHeader header;
|
||||
PLOpenhashElement element[1];
|
||||
/* more elements follow */
|
||||
} PLOpenhash;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PR_EXTERN(PRStatus)
|
||||
PL_OpenhashInit(PLOpenhash* self, PRUint32 initialSize,
|
||||
PLOpenhashHashFun hash, PLOpenhashEqualFun equal,
|
||||
PLOpenhashDestroyFun del, void* context);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_OpenhashCleanup(PLOpenhash* self);
|
||||
|
||||
PR_EXTERN(PLOpenhash*)
|
||||
PL_NewOpenhash(PRUint32 initialSize,
|
||||
PLOpenhashHashFun hash, PLOpenhashEqualFun equal,
|
||||
PLOpenhashDestroyFun del, void* context);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_OpenhashDestroy(PLOpenhash* self);
|
||||
|
||||
PR_EXTERN(void*)
|
||||
PL_OpenhashLookup(PLOpenhash* self, PRUword key);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_OpenhashAdd(PLOpenhash* self, PRUword key, void* value);
|
||||
|
||||
PR_EXTERN(PRStatus)
|
||||
PL_OpenhashRemove(PLOpenhash* self, PRUword key);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_OpenhashEmpty(PLOpenhash* self);
|
||||
|
||||
#define PL_OpenhashSize(self) ((self)->keyCount)
|
||||
|
||||
/******************************************************************************/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* openhash_h__ */
|
||||
/******************************************************************************/
|
504
ef/gc/hash/plhash.c
Normal file
504
ef/gc/hash/plhash.c
Normal file
@ -0,0 +1,504 @@
|
||||
/* Insert copyright and license here 1997
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PL hash table package.
|
||||
*/
|
||||
#include "plhash.h"
|
||||
#include "prbit.h"
|
||||
#include "prlog.h"
|
||||
#include "prmem.h"
|
||||
#include "prtypes.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Compute the number of buckets in ht */
|
||||
#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
|
||||
|
||||
/* The smallest table has 16 buckets */
|
||||
#define MINBUCKETSLOG2 4
|
||||
#define MINBUCKETS (1 << MINBUCKETSLOG2)
|
||||
|
||||
/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
|
||||
#define OVERLOADED(n) ((n) - ((n) >> 3))
|
||||
|
||||
/* Compute the number of entries below which we shrink the table by half */
|
||||
#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
|
||||
|
||||
/*
|
||||
** Stubs for default hash allocator ops.
|
||||
*/
|
||||
static void * PR_CALLBACK
|
||||
DefaultAllocTable(void *pool, PRSize size)
|
||||
{
|
||||
#if defined(XP_MAC)
|
||||
#pragma unused (pool)
|
||||
#endif
|
||||
|
||||
return PR_MALLOC(size);
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
DefaultFreeTable(void *pool, void *item)
|
||||
{
|
||||
#if defined(XP_MAC)
|
||||
#pragma unused (pool)
|
||||
#endif
|
||||
|
||||
PR_DELETE(item);
|
||||
}
|
||||
|
||||
static PLHashEntry * PR_CALLBACK
|
||||
DefaultAllocEntry(void *pool, const void *key)
|
||||
{
|
||||
#if defined(XP_MAC)
|
||||
#pragma unused (pool,key)
|
||||
#endif
|
||||
|
||||
return PR_NEW(PLHashEntry);
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
|
||||
{
|
||||
#if defined(XP_MAC)
|
||||
#pragma unused (pool)
|
||||
#endif
|
||||
|
||||
if (flag == HT_FREE_ENTRY)
|
||||
PR_DELETE(he);
|
||||
}
|
||||
|
||||
static PLHashAllocOps defaultHashAllocOps = {
|
||||
DefaultAllocTable, DefaultFreeTable,
|
||||
DefaultAllocEntry, DefaultFreeEntry
|
||||
};
|
||||
|
||||
PR_IMPLEMENT(PLHashTable *)
|
||||
PL_NewHashTable(PRUint32 n, PLHashFunction keyHash,
|
||||
PLHashComparator keyCompare, PLHashComparator valueCompare,
|
||||
PLHashAllocOps *allocOps, void *allocPriv)
|
||||
{
|
||||
PLHashTable *ht;
|
||||
PRStatus status;
|
||||
|
||||
if (!allocOps) allocOps = &defaultHashAllocOps;
|
||||
|
||||
ht = (*allocOps->allocTable)(allocPriv, sizeof *ht);
|
||||
if (!ht)
|
||||
return 0;
|
||||
status = PL_HashTableInit(ht, n, keyHash, keyCompare, valueCompare,
|
||||
allocOps, allocPriv);
|
||||
if (status != PR_SUCCESS) {
|
||||
(*allocOps->freeTable)(allocPriv, ht);
|
||||
return 0;
|
||||
}
|
||||
return ht;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PRStatus)
|
||||
PL_HashTableInit(PLHashTable *ht, PRUint32 n, PLHashFunction keyHash,
|
||||
PLHashComparator keyCompare, PLHashComparator valueCompare,
|
||||
PLHashAllocOps *allocOps, void *allocPriv)
|
||||
{
|
||||
PRUint32 nb;
|
||||
|
||||
if (n <= MINBUCKETS) {
|
||||
n = MINBUCKETSLOG2;
|
||||
} else {
|
||||
n = PR_CeilingLog2(n);
|
||||
if ((PRInt32)n < 0)
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
if (!allocOps) allocOps = &defaultHashAllocOps;
|
||||
|
||||
ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
|
||||
if (!ht)
|
||||
return 0;
|
||||
memset(ht, 0, sizeof *ht);
|
||||
ht->shift = PL_HASH_BITS - n;
|
||||
n = 1 << n;
|
||||
#if defined(XP_PC) && !defined(_WIN32)
|
||||
if (n > 16000) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
#endif /* WIN16 */
|
||||
nb = n * sizeof(PLHashEntry *);
|
||||
ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
|
||||
if (!ht->buckets) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
|
||||
ht->keyHash = keyHash;
|
||||
ht->keyCompare = keyCompare;
|
||||
ht->valueCompare = valueCompare;
|
||||
ht->allocOps = allocOps;
|
||||
ht->allocPriv = allocPriv;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_HashTableCleanup(PLHashTable *ht)
|
||||
{
|
||||
PRUint32 i, n;
|
||||
PLHashEntry *he, *next;
|
||||
PLHashAllocOps *allocOps = ht->allocOps;
|
||||
void *allocPriv = ht->allocPriv;
|
||||
|
||||
n = NBUCKETS(ht);
|
||||
for (i = 0; i < n; i++) {
|
||||
for (he = ht->buckets[i]; he; he = next) {
|
||||
next = he->next;
|
||||
(*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
|
||||
#endif
|
||||
(*allocOps->freeTable)(allocPriv, ht->buckets);
|
||||
#ifdef DEBUG
|
||||
memset(ht, 0xDB, sizeof *ht);
|
||||
#endif
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_HashTableDestroy(PLHashTable *ht)
|
||||
{
|
||||
PLHashAllocOps *allocOps = ht->allocOps;
|
||||
void *allocPriv = ht->allocPriv;
|
||||
PL_HashTableCleanup(ht);
|
||||
(*allocOps->freeTable)(allocPriv, ht);
|
||||
}
|
||||
|
||||
/*
|
||||
** Multiplicative hash, from Knuth 6.4.
|
||||
*/
|
||||
#define GOLDEN_RATIO 0x9E3779B9U
|
||||
|
||||
PR_IMPLEMENT(PLHashEntry **)
|
||||
PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key)
|
||||
{
|
||||
PLHashEntry *he, **hep, **hep0;
|
||||
PLHashNumber h;
|
||||
|
||||
#ifdef HASHMETER
|
||||
ht->nlookups++;
|
||||
#endif
|
||||
h = keyHash * GOLDEN_RATIO;
|
||||
h >>= ht->shift;
|
||||
hep = hep0 = &ht->buckets[h];
|
||||
while ((he = *hep) != 0) {
|
||||
if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
|
||||
/* Move to front of chain if not already there */
|
||||
if (hep != hep0) {
|
||||
*hep = he->next;
|
||||
he->next = *hep0;
|
||||
*hep0 = he;
|
||||
}
|
||||
return hep0;
|
||||
}
|
||||
hep = &he->next;
|
||||
#ifdef HASHMETER
|
||||
ht->nsteps++;
|
||||
#endif
|
||||
}
|
||||
return hep;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PLHashEntry *)
|
||||
PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep,
|
||||
PLHashNumber keyHash, const void *key, void *value)
|
||||
{
|
||||
PRUint32 i, n;
|
||||
PLHashEntry *he, *next, **oldbuckets;
|
||||
PRUint32 nb;
|
||||
|
||||
/* Grow the table if it is overloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (ht->nentries >= OVERLOADED(n)) {
|
||||
#ifdef HASHMETER
|
||||
ht->ngrows++;
|
||||
#endif
|
||||
ht->shift--;
|
||||
oldbuckets = ht->buckets;
|
||||
#if defined(XP_PC) && !defined(_WIN32)
|
||||
if (2 * n > 16000)
|
||||
return 0;
|
||||
#endif /* WIN16 */
|
||||
nb = 2 * n * sizeof(PLHashEntry *);
|
||||
ht->buckets = (PLHashEntry**)
|
||||
((*ht->allocOps->allocTable)(ht->allocPriv, nb));
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return 0;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (he = oldbuckets[i]; he; he = next) {
|
||||
next = he->next;
|
||||
hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
|
||||
PR_ASSERT(*hep == 0);
|
||||
he->next = 0;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
|
||||
hep = PL_HashTableRawLookup(ht, keyHash, key);
|
||||
}
|
||||
|
||||
/* Make a new key value entry */
|
||||
he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
|
||||
if (!he)
|
||||
return 0;
|
||||
he->keyHash = keyHash;
|
||||
he->key = key;
|
||||
he->value = value;
|
||||
he->next = *hep;
|
||||
*hep = he;
|
||||
ht->nentries++;
|
||||
return he;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PLHashEntry *)
|
||||
PL_HashTableAdd(PLHashTable *ht, const void *key, void *value)
|
||||
{
|
||||
PLHashNumber keyHash;
|
||||
PLHashEntry *he, **hep;
|
||||
|
||||
keyHash = (*ht->keyHash)(key);
|
||||
hep = PL_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) != 0) {
|
||||
/* Hit; see if values match */
|
||||
if ((*ht->valueCompare)(he->value, value)) {
|
||||
/* key,value pair is already present in table */
|
||||
return he;
|
||||
}
|
||||
if (he->value)
|
||||
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
|
||||
he->value = value;
|
||||
return he;
|
||||
}
|
||||
return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he)
|
||||
{
|
||||
PRUint32 i, n;
|
||||
PLHashEntry *next, **oldbuckets;
|
||||
PRUint32 nb;
|
||||
|
||||
*hep = he->next;
|
||||
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
|
||||
/* Shrink table if it's underloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (--ht->nentries < UNDERLOADED(n)) {
|
||||
#ifdef HASHMETER
|
||||
ht->nshrinks++;
|
||||
#endif
|
||||
ht->shift++;
|
||||
oldbuckets = ht->buckets;
|
||||
nb = n * sizeof(PLHashEntry*) / 2;
|
||||
ht->buckets = (PLHashEntry**)(
|
||||
(*ht->allocOps->allocTable)(ht->allocPriv, nb));
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (he = oldbuckets[i]; he; he = next) {
|
||||
next = he->next;
|
||||
hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
|
||||
PR_ASSERT(*hep == 0);
|
||||
he->next = 0;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
|
||||
}
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PRBool)
|
||||
PL_HashTableRemove(PLHashTable *ht, const void *key)
|
||||
{
|
||||
PLHashNumber keyHash;
|
||||
PLHashEntry *he, **hep;
|
||||
|
||||
keyHash = (*ht->keyHash)(key);
|
||||
hep = PL_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) == 0)
|
||||
return PR_FALSE;
|
||||
|
||||
/* Hit; remove element */
|
||||
PL_HashTableRawRemove(ht, hep, he);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void *)
|
||||
PL_HashTableLookup(PLHashTable *ht, const void *key)
|
||||
{
|
||||
PLHashNumber keyHash;
|
||||
PLHashEntry *he, **hep;
|
||||
|
||||
keyHash = (*ht->keyHash)(key);
|
||||
hep = PL_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) != 0) {
|
||||
return he->value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterate over the entries in the hash table calling func for each
|
||||
** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
|
||||
** Return a count of the number of elements scanned.
|
||||
*/
|
||||
PR_IMPLEMENT(int)
|
||||
PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg)
|
||||
{
|
||||
PLHashEntry *he, **hep;
|
||||
PRUint32 i, nbuckets;
|
||||
int rv, n = 0;
|
||||
PLHashEntry *todo = 0;
|
||||
|
||||
nbuckets = NBUCKETS(ht);
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
hep = &ht->buckets[i];
|
||||
while ((he = *hep) != 0) {
|
||||
rv = (*f)(he, n, arg);
|
||||
n++;
|
||||
if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
|
||||
*hep = he->next;
|
||||
if (rv & HT_ENUMERATE_REMOVE) {
|
||||
he->next = todo;
|
||||
todo = he;
|
||||
}
|
||||
} else {
|
||||
hep = &he->next;
|
||||
}
|
||||
if (rv & HT_ENUMERATE_STOP) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
hep = &todo;
|
||||
while ((he = *hep) != 0) {
|
||||
PL_HashTableRawRemove(ht, hep, he);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef HASHMETER
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
PL_HashTableDumpMeter(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
|
||||
{
|
||||
double mean, variance;
|
||||
PRUint32 nchains, nbuckets;
|
||||
PRUint32 i, n, maxChain, maxChainLen;
|
||||
PLHashEntry *he;
|
||||
|
||||
variance = 0;
|
||||
nchains = 0;
|
||||
maxChainLen = 0;
|
||||
nbuckets = NBUCKETS(ht);
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
he = ht->buckets[i];
|
||||
if (!he)
|
||||
continue;
|
||||
nchains++;
|
||||
for (n = 0; he; he = he->next)
|
||||
n++;
|
||||
variance += n * n;
|
||||
if (n > maxChainLen) {
|
||||
maxChainLen = n;
|
||||
maxChain = i;
|
||||
}
|
||||
}
|
||||
mean = (double)ht->nentries / nchains;
|
||||
variance = fabs(variance / nchains - mean * mean);
|
||||
|
||||
fprintf(fp, "\nHash table statistics:\n");
|
||||
fprintf(fp, " number of lookups: %u\n", ht->nlookups);
|
||||
fprintf(fp, " number of entries: %u\n", ht->nentries);
|
||||
fprintf(fp, " number of grows: %u\n", ht->ngrows);
|
||||
fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
|
||||
fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
|
||||
/ ht->nlookups);
|
||||
fprintf(fp, "mean hash chain length: %g\n", mean);
|
||||
fprintf(fp, " standard deviation: %g\n", sqrt(variance));
|
||||
fprintf(fp, " max hash chain length: %u\n", maxChainLen);
|
||||
fprintf(fp, " max hash chain: [%u]\n", maxChain);
|
||||
|
||||
for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
|
||||
if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
|
||||
break;
|
||||
}
|
||||
#endif /* HASHMETER */
|
||||
|
||||
PR_IMPLEMENT(int)
|
||||
PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = PL_HashTableEnumerateEntries(ht, dump, fp);
|
||||
#ifdef HASHMETER
|
||||
PL_HashTableDumpMeter(ht, dump, fp);
|
||||
#endif
|
||||
return count;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(PLHashNumber)
|
||||
PL_HashString(const void *key)
|
||||
{
|
||||
PLHashNumber h;
|
||||
const PRUint8 *s;
|
||||
|
||||
h = 0;
|
||||
for (s = (const PRUint8*)key; *s; s++)
|
||||
h = (h >> 28) ^ (h << 4) ^ *s;
|
||||
return h;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(int)
|
||||
PL_CompareStrings(const void *v1, const void *v2)
|
||||
{
|
||||
return strcmp((const char*)v1, (const char*)v2) == 0;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(int)
|
||||
PL_CompareValues(const void *v1, const void *v2)
|
||||
{
|
||||
return v1 == v2;
|
||||
}
|
145
ef/gc/hash/plhash.h
Normal file
145
ef/gc/hash/plhash.h
Normal file
@ -0,0 +1,145 @@
|
||||
/* Insert copyright and license here 1997
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef plhash_h___
|
||||
#define plhash_h___
|
||||
/*
|
||||
* API to portable hash table code.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include "prtypes.h"
|
||||
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
typedef struct PLHashEntry PLHashEntry;
|
||||
typedef struct PLHashTable PLHashTable;
|
||||
typedef PRUint32 PLHashNumber;
|
||||
#define PL_HASH_BITS 32
|
||||
typedef PLHashNumber (PR_CALLBACK *PLHashFunction)(const void *key);
|
||||
typedef PRIntn (PR_CALLBACK *PLHashComparator)(const void *v1, const void *v2);
|
||||
typedef PRIntn (PR_CALLBACK *PLHashEnumerator)(PLHashEntry *he, PRIntn i, void *arg);
|
||||
|
||||
/* Flag bits in PLHashEnumerator's return value */
|
||||
#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */
|
||||
#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */
|
||||
#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */
|
||||
#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */
|
||||
|
||||
typedef struct PLHashAllocOps {
|
||||
void * (PR_CALLBACK *allocTable)(void *pool, PRSize size);
|
||||
void (PR_CALLBACK *freeTable)(void *pool, void *item);
|
||||
PLHashEntry * (PR_CALLBACK *allocEntry)(void *pool, const void *key);
|
||||
void (PR_CALLBACK *freeEntry)(void *pool, PLHashEntry *he, PRUintn flag);
|
||||
} PLHashAllocOps;
|
||||
|
||||
#define HT_FREE_VALUE 0 /* just free the entry's value */
|
||||
#define HT_FREE_ENTRY 1 /* free value and entire entry */
|
||||
|
||||
struct PLHashEntry {
|
||||
PLHashEntry *next; /* hash chain linkage */
|
||||
PLHashNumber keyHash; /* key hash function result */
|
||||
const void *key; /* ptr to opaque key */
|
||||
void *value; /* ptr to opaque value */
|
||||
};
|
||||
|
||||
struct PLHashTable {
|
||||
PLHashEntry **buckets; /* vector of hash buckets */
|
||||
PRUint32 nentries; /* number of entries in table */
|
||||
PRUint32 shift; /* multiplicative hash shift */
|
||||
PLHashFunction keyHash; /* key hash function */
|
||||
PLHashComparator keyCompare; /* key comparison function */
|
||||
PLHashComparator valueCompare; /* value comparison function */
|
||||
PLHashAllocOps *allocOps; /* allocation operations */
|
||||
void *allocPriv; /* allocation private data */
|
||||
#ifdef HASHMETER
|
||||
PRUint32 nlookups; /* total number of lookups */
|
||||
PRUint32 nsteps; /* number of hash chains traversed */
|
||||
PRUint32 ngrows; /* number of table expansions */
|
||||
PRUint32 nshrinks; /* number of table contractions */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a new hash table.
|
||||
* If allocOps is null, use default allocator ops built on top of malloc().
|
||||
*/
|
||||
PR_EXTERN(PLHashTable *)
|
||||
PL_NewHashTable(PRUint32 n, PLHashFunction keyHash,
|
||||
PLHashComparator keyCompare, PLHashComparator valueCompare,
|
||||
PLHashAllocOps *allocOps, void *allocPriv);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_HashTableDestroy(PLHashTable *ht);
|
||||
|
||||
/*
|
||||
* Initialize an existing HashTable struct.
|
||||
*/
|
||||
PR_IMPLEMENT(PRStatus)
|
||||
PL_HashTableInit(PLHashTable *ht, PRUint32 n, PLHashFunction keyHash,
|
||||
PLHashComparator keyCompare, PLHashComparator valueCompare,
|
||||
PLHashAllocOps *allocOps, void *allocPriv);
|
||||
|
||||
/*
|
||||
* Cleanup (destroy contents of) existing HashTable struct.
|
||||
*/
|
||||
PR_IMPLEMENT(void)
|
||||
PL_HashTableCleanup(PLHashTable *ht);
|
||||
|
||||
/* Low level access methods */
|
||||
PR_EXTERN(PLHashEntry **)
|
||||
PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key);
|
||||
|
||||
PR_EXTERN(PLHashEntry *)
|
||||
PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep, PLHashNumber keyHash,
|
||||
const void *key, void *value);
|
||||
|
||||
PR_EXTERN(void)
|
||||
PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he);
|
||||
|
||||
/* Higher level access methods */
|
||||
PR_EXTERN(PLHashEntry *)
|
||||
PL_HashTableAdd(PLHashTable *ht, const void *key, void *value);
|
||||
|
||||
PR_EXTERN(PRBool)
|
||||
PL_HashTableRemove(PLHashTable *ht, const void *key);
|
||||
|
||||
PR_EXTERN(PRIntn)
|
||||
PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg);
|
||||
|
||||
PR_EXTERN(void *)
|
||||
PL_HashTableLookup(PLHashTable *ht, const void *key);
|
||||
|
||||
PR_EXTERN(PRIntn)
|
||||
PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp);
|
||||
|
||||
/* General-purpose C string hash function. */
|
||||
PR_EXTERN(PLHashNumber)
|
||||
PL_HashString(const void *key);
|
||||
|
||||
/* Compare strings using strcmp(), return true if equal. */
|
||||
PR_EXTERN(int)
|
||||
PL_CompareStrings(const void *v1, const void *v2);
|
||||
|
||||
/* Stub function just returns v1 == v2 */
|
||||
PR_EXTERN(PRIntn)
|
||||
PL_CompareValues(const void *v1, const void *v2);
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
#endif /* plhash_h___ */
|
2
ef/gc/include/Makefile
Normal file
2
ef/gc/include/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
include manifest.mn
|
||||
include $(DEPTH)/config/rules.mk
|
49
ef/gc/include/blank.h
Normal file
49
ef/gc/include/blank.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __BLANK__
|
||||
#define __BLANK__
|
||||
|
||||
#include "smpriv.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __BLANK__ */
|
||||
/******************************************************************************/
|
9
ef/gc/include/makefile.win
Normal file
9
ef/gc/include/makefile.win
Normal file
@ -0,0 +1,9 @@
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Makefile to install MODULES/APPLET/INCLUDE header files into the distribution
|
||||
#// directory.
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
include <manifest.mn>
|
||||
include <$(DEPTH)/config/rules.mak>
|
||||
|
16
ef/gc/include/manifest.mn
Normal file
16
ef/gc/include/manifest.mn
Normal file
@ -0,0 +1,16 @@
|
||||
MODULE = sm
|
||||
|
||||
DEPTH = ../..
|
||||
|
||||
EXPORTS = \
|
||||
sm.h \
|
||||
smobj.h \
|
||||
smgen.h \
|
||||
smheap.h \
|
||||
smmalloc.h \
|
||||
smpage.h \
|
||||
smpool.h \
|
||||
smpriv.h \
|
||||
smstack.h \
|
||||
smtrav.h \
|
||||
smhash.h
|
353
ef/gc/include/sm.h
Normal file
353
ef/gc/include/sm.h
Normal file
@ -0,0 +1,353 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
** SportModel is a hybrid, mark-and-sweep, mostly-copying, card-marking
|
||||
** generational garbage collector designed for very low level-two cache miss
|
||||
** rates. The intended use is for the ElectricalFire Java implementation,
|
||||
** although it is not ElectricalFire or Java specific. It also includes an
|
||||
** integrated malloc/free package.
|
||||
*/
|
||||
|
||||
#ifndef __SM__
|
||||
#define __SM__
|
||||
|
||||
#include "smpriv.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Types and Constants
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMHeap SMHeap;
|
||||
typedef union SMClass SMClass;
|
||||
typedef struct SMObjectClass SMObjectClass;
|
||||
typedef struct SMArrayClass SMArrayClass;
|
||||
typedef struct SMPrimClass SMPrimClass;
|
||||
typedef struct SMObject SMObject;
|
||||
typedef struct SMArray SMArray;
|
||||
typedef struct SMObjStruct SMObjStruct;
|
||||
typedef struct SMArrayStruct SMArrayStruct;
|
||||
|
||||
typedef enum SMGenNum {
|
||||
SMGenNum_Freshman = 0, /* for new allocations */
|
||||
SMGenNum_Sophomore, /* first survivor generation */
|
||||
SMGenNum_Junior, /* oldest generation for applets */
|
||||
SMGenNum_Senior, /* oldest generation for system code */
|
||||
SMGenNum_Static, /* static space -- scanned, but not marked */
|
||||
SMGenNum_Malloc, /* malloc space -- explicitly freed */
|
||||
SMGenNum_Free = 7 /* pages unused by malloc or gc */
|
||||
} SMGenNum;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_DECLARE_ENSURE(SMObject)
|
||||
SM_DECLARE_ENSURE(SMObjStruct)
|
||||
SM_DECLARE_ENSURE(SMArray)
|
||||
SM_DECLARE_ENSURE(SMArrayStruct)
|
||||
|
||||
#define SM_ENCRYPT_OBJECT(obj) ((SMObject*)SM_ENCRYPT(SM_ENSURE(SMObjStruct, obj)))
|
||||
#define SM_DECRYPT_OBJECT(obj) ((SMObjStruct*)SM_DECRYPT(SM_ENSURE(SMObject, obj)))
|
||||
|
||||
#define SM_ENCRYPT_ARRAY(arr) ((SMArray*)SM_ENCRYPT(SM_ENSURE(SMArrayStruct, arr)))
|
||||
#define SM_DECRYPT_ARRAY(arr) ((SMArrayStruct*)SM_DECRYPT(SM_ENSURE(SMArray, arr)))
|
||||
|
||||
/* XXX add ifdef SM_COLLECT_CLASSES */
|
||||
#define SM_ENCRYPT_CLASS(cl) (cl)
|
||||
#define SM_DECRYPT_CLASS(cl) (cl)
|
||||
|
||||
/*******************************************************************************
|
||||
* Startup/Shutdown Routines
|
||||
******************************************************************************/
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_MarkRoots(SMObject** base, PRUword count, PRBool conservative);
|
||||
|
||||
/* The user must define a root marking routine with the following signature.
|
||||
It's job is to call SM_MarkRoots for all roots in the program. This routine
|
||||
is registered by calling SM_InitHeap. */
|
||||
typedef void
|
||||
(*SMMarkRootsProc)(SMGenNum collectingGenNum, PRBool copyCycle, void* closure);
|
||||
|
||||
/* This function must be called as the first thing in the user's SMMarkRootsProc
|
||||
(or _be_ the user's SMMarkRootsProc if there are no other roots) if NSPR
|
||||
threads are to be scanned conservatively. Failure to call this indicates
|
||||
either that you know what you're doing, or you have a bug. */
|
||||
SM_EXTERN(void)
|
||||
SM_MarkThreadsConservatively(SMGenNum collectingGenNum, PRBool copyCycle,
|
||||
void* closure);
|
||||
|
||||
/* This function allows finer grained control over exactly which threads are
|
||||
scanned conservatively. */
|
||||
SM_EXTERN(void)
|
||||
SM_MarkThreadConservatively(PRThread* thread, SMGenNum collectingGenNum,
|
||||
PRBool copyCycle, void* closure);
|
||||
|
||||
/* The SMGCHookProc defines the signature of the before and after gc hook
|
||||
procedures. These are called before and after a collection on the thread
|
||||
which caused the collection. The primary function of these hooks is to allow
|
||||
any locks needed by the root marking procedure to be entered (and exited)
|
||||
because once the collection process has started, all other threads are
|
||||
suspended, and no locks (or monitors) can be waited on. */
|
||||
typedef void
|
||||
(*SMGCHookProc)(SMGenNum collectingGenNum, PRUword collectCount,
|
||||
PRBool copyCycle, void* closure);
|
||||
|
||||
/* Initialize the garbage collector system. */
|
||||
SM_EXTERN(PRStatus)
|
||||
SM_InitGC(PRUword initialHeapSize, PRUword maxHeapSize,
|
||||
SMGCHookProc beforeGCHook, void* beforeGCClosure,
|
||||
SMGCHookProc afterGCHook, void* afterGCClosure,
|
||||
SMMarkRootsProc markRootsProc, void* markRootsClosure);
|
||||
|
||||
/* Shutdown the garbage collector system. */
|
||||
SM_EXTERN(PRStatus)
|
||||
SM_CleanupGC(PRBool finalizeOnExit);
|
||||
|
||||
/* Initialize the malloc system. This is done implicitly when SM_InitGC is called. */
|
||||
SM_EXTERN(PRStatus)
|
||||
SM_InitMalloc(PRUword initialHeapSize, PRUword maxHeapSize);
|
||||
|
||||
/* Shutdown the malloc system. This is done implicitly when SM_CleanupGC is called. */
|
||||
SM_EXTERN(PRStatus)
|
||||
SM_CleanupMalloc(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* Allocation and Collection
|
||||
******************************************************************************/
|
||||
|
||||
/* Allocates a garbage-collectable object of a given class.
|
||||
The class must be constructed via SM_InitObjectClass. */
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_AllocObject(SMObjectClass* clazz);
|
||||
|
||||
/* Allocates a garbage-collectable array object of a given class.
|
||||
The class must be constructed via SM_InitArrayClass. */
|
||||
SM_EXTERN(SMArray*)
|
||||
SM_AllocArray(SMArrayClass* clazz, PRUword size);
|
||||
|
||||
/* Allocates a garbage-collectable object of a given class, but with extra space
|
||||
at the end of the object. The size of the extra space is specified in bytes
|
||||
and is not examined by the garbage collector. A pointer to the beginning of
|
||||
the extra space can be obtained by SM_OBJECT_EXTRA(obj);
|
||||
The class must be constructed via SM_InitObjectClass. */
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_AllocObjectExtra(SMObjectClass* clazz, PRUword extraBytes);
|
||||
|
||||
/* Returns the size of an object in bytes. This function cannot be used on
|
||||
malloc'd objects. */
|
||||
SM_EXTERN(PRUword)
|
||||
SM_ObjectSize(SMObject* obj);
|
||||
|
||||
/* Returns a new object which is a shallow copy of the original. */
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_Clone(SMObject* obj);
|
||||
|
||||
/* Forces a garbage collection of the entire heap. */
|
||||
SM_EXTERN(void)
|
||||
SM_Collect(void);
|
||||
|
||||
/* Forces finalization to occur. */
|
||||
SM_EXTERN(void)
|
||||
SM_RunFinalization(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* Malloc and Friends
|
||||
*
|
||||
* These memory management routines may be used as an alternative to malloc,
|
||||
* free and associated routines. It is an exercise to the application
|
||||
* programmer to cause these routines to be linked as _the_ malloc and free
|
||||
* routines for the application. The garbage collector itself uses these
|
||||
* routines internally and does not otherwise depend on malloc, free, etc.
|
||||
******************************************************************************/
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_Malloc(PRUword size);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_Free(void* ptr);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_Calloc(PRUword size, PRUword count);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_Realloc(void* ptr, PRUword size);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_Strdup(void* str);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_Strndup(void* str, PRUword size);
|
||||
|
||||
/*******************************************************************************
|
||||
* Memory Pool variation of Malloc and Friends
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMPool SMPool;
|
||||
|
||||
SM_EXTERN(SMPool*)
|
||||
SM_NewPool(void);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DeletePool(SMPool* pool);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_PoolMalloc(SMPool* pool, PRUword size);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_PoolFree(SMPool* pool, void* ptr);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_PoolCalloc(SMPool* pool, PRUword size, PRUword count);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_PoolRealloc(SMPool* pool, void* ptr, PRUword size);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_PoolStrdup(SMPool* pool, void* str);
|
||||
|
||||
SM_EXTERN(void*)
|
||||
SM_PoolStrndup(SMPool* pool, void* str, PRUword size);
|
||||
|
||||
/*******************************************************************************
|
||||
* Write Barrier
|
||||
*
|
||||
* All assignments to garbage collected object fields must go through the write
|
||||
* barrier. This allows the collector to focus on a smaller portion of the heap
|
||||
* during a collection (a "generation") while remaining informed about changes
|
||||
* made to objects of other generations.
|
||||
*
|
||||
* To set a field of an object or an element of an array, rather than writing:
|
||||
*
|
||||
* obj->fieldName = value;
|
||||
* arr[i] = value;
|
||||
*
|
||||
* one must instead write the following:
|
||||
*
|
||||
* SM_SET_FIELD(ObjType, obj, fieldName, value);
|
||||
* SM_SET_ELEMENT(ObjType, arr, i, value);
|
||||
*
|
||||
* Failure to use the SM_SET macros for all assignments will ultimately result
|
||||
* in system failure (i.e. a crash). But in order to aid in tracking down
|
||||
* problems related to this, the SM_CHECK_PTRS preprocessor symbol may be
|
||||
* defined which causes all object pointers to be "encrypted" in such a way
|
||||
* that any attempt to access an object pointer outside of the SM_SET
|
||||
* macros will cause an immediate crash. Crashes of this sort point to places
|
||||
* in the code where the macros were forgotten. However, use of this encryption
|
||||
* technique also requires all read accesses to go through an equivalent pair
|
||||
* of macros, e.g. rather than writing:
|
||||
*
|
||||
* obj->fieldName
|
||||
* arr[i]
|
||||
*
|
||||
* one must instead write the following:
|
||||
*
|
||||
* SM_GET_FIELD(ObjType, obj, fieldName)
|
||||
* SM_GET_ELEMENT(ObjType, arr, i)
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
SM_EXTERN(PRUint8*) SM_CardTable;
|
||||
|
||||
/* Accessors for debugging: */
|
||||
|
||||
#define SM_GET_ELEMENT(ObjType, obj, index) \
|
||||
(((ObjType*)SM_DECRYPT(obj))[index]) \
|
||||
|
||||
#define SM_GET_FIELD(ObjType, obj, fieldName) \
|
||||
(((ObjType*)SM_DECRYPT(obj))->fieldName) \
|
||||
|
||||
/* Unsafe assignment -- only to be used if you really know what you're
|
||||
doing, e.g. initializing a newly created object's fields: */
|
||||
|
||||
#define SM_SET_ELEMENT_UNSAFE(ObjType, obj, index, value) \
|
||||
(SM_GET_ELEMENT(ObjType, obj, index) = (value)) \
|
||||
|
||||
#define SM_SET_FIELD_UNSAFE(ObjType, obj, fieldName, value) \
|
||||
(SM_GET_FIELD(ObjType, obj, fieldName) = (value)) \
|
||||
|
||||
#ifdef SM_NO_WRITE_BARRIER
|
||||
|
||||
#define SM_OBJECT_CARDDESC(obj) NULL
|
||||
#define SM_DIRTY_PAGE(addr) ((void)0)
|
||||
#define SM_CLEAN_PAGE(addr) ((void)0)
|
||||
|
||||
#else /* !SM_NO_WRITE_BARRIER */
|
||||
|
||||
#define SM_OBJECT_CARDDESC(obj) (&SM_CardTable[SM_PAGE_NUMBER(SM_DECRYPT_OBJECT(obj))])
|
||||
#define SM_CARDDESC_GEN(cd) ((SMGenNum)((cd) - 1))
|
||||
#define SM_GEN_CARDDESC(genNum) ((PRUint8)(genNum) + 1)
|
||||
|
||||
#ifdef DEBUG
|
||||
SM_EXTERN(void) SM_DirtyPage(void* addr);
|
||||
SM_EXTERN(void) SM_CleanPage(void* addr);
|
||||
#define SM_DIRTY_PAGE(addr) SM_DirtyPage(addr)
|
||||
#define SM_CLEAN_PAGE(addr) SM_CleanPage(addr)
|
||||
#else /* DEBUG */
|
||||
#define SM_DIRTY_PAGE(addr) (*SM_OBJECT_CARDDESC(addr) = 0)
|
||||
#define SM_CLEAN_PAGE(addr) (*SM_OBJECT_CARDDESC(addr) = SM_GEN_CARDDESC(SMGenNum_Free))
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* !SM_NO_WRITE_BARRIER */
|
||||
|
||||
/* Safe assignment that uses the write barrier: */
|
||||
|
||||
#define SM_SET_ELEMENT(ObjType, obj, index, value) \
|
||||
{ \
|
||||
ObjType* _obj = obj; \
|
||||
SM_SET_ELEMENT_UNSAFE(ObjType, _obj, index, value); \
|
||||
SM_DIRTY_PAGE(_obj); \
|
||||
} \
|
||||
|
||||
#define SM_SET_FIELD(ObjType, obj, fieldName, value) \
|
||||
{ \
|
||||
ObjType* _obj = obj; \
|
||||
SM_SET_FIELD_UNSAFE(ObjType, _obj, fieldName, value); \
|
||||
SM_DIRTY_PAGE(_obj); \
|
||||
} \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_SetTraceOutputFile(FILE* out);
|
||||
|
||||
#endif /* defined(DEBUG) || defined(SM_DUMP) */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SM__ */
|
||||
/******************************************************************************/
|
114
ef/gc/include/smgen.h
Normal file
114
ef/gc/include/smgen.h
Normal file
@ -0,0 +1,114 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMGEN__
|
||||
#define __SMGEN__
|
||||
|
||||
#include "smpool.h"
|
||||
#include "smobj.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Generations
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_GEN_COUNT (SMGenNum_Static - SMGenNum_Freshman + 1)
|
||||
|
||||
#define SM_IS_GC_SPACE(genNum) ((genNum) < SMGenNum_Malloc)
|
||||
#define SM_IS_MALLOC_SPACE(genNum) ((genNum) == SMGenNum_Malloc)
|
||||
#define SM_IS_FREE_SPACE(genNum) ((genNum) == SMGenNum_Free)
|
||||
|
||||
typedef struct SMGen {
|
||||
SMPool pool;
|
||||
PRUword allocThreshold;
|
||||
PRUword collectCount;
|
||||
} SMGen;
|
||||
|
||||
#define SM_DEFAULT_ALLOC_THRESHOLD (16 * 1024) /* XXX ??? */
|
||||
|
||||
extern PRStatus
|
||||
sm_InitGen(SMGen* gen, PRUword allocThreshold);
|
||||
|
||||
extern void
|
||||
sm_FiniGen(SMGen* gen);
|
||||
|
||||
/* Like sm_PoolAllocObj, but is only used to allocate an object in a new
|
||||
generation during gc. */
|
||||
extern SMObjStruct*
|
||||
sm_GenPromoteObj(SMGen* gen, SMBucket bucket);
|
||||
|
||||
/*******************************************************************************
|
||||
* Generation Operations
|
||||
*
|
||||
* Unlike their pool equivalents, these operations update the card-table.
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_GEN_ALLOC_OBJ(result, gen, bucket) \
|
||||
(result = (SMObjStruct*)sm_PoolAllocObj(&(gen)->pool, bucket), \
|
||||
(result ? SM_DIRTY_PAGE(result) : (void)0)) \
|
||||
|
||||
#define SM_GEN_DESTROY_PAGE(gen, freePd) \
|
||||
{ \
|
||||
SMPageDesc* _pd1 = (freePd); \
|
||||
SMPage* _page1 = SM_PAGEDESC_PAGE(_pd1); \
|
||||
SM_CLEAN_PAGE(_page1); \
|
||||
SM_POOL_DESTROY_PAGE(&(gen)->pool, _pd1); \
|
||||
} \
|
||||
|
||||
#define SM_GEN_ALLOC_LARGE_OBJ(result, gen, genNum, size) \
|
||||
(result = (SMObjStruct*)sm_PoolAllocLargeObj(&(gen)->pool, genNum, size), \
|
||||
(result ? SM_DIRTY_PAGE(result) : (void)0)) \
|
||||
|
||||
#define SM_GEN_FREE_LARGE_OBJ(gen, obj) \
|
||||
{ \
|
||||
SMObjStruct* _obj1 = (obj); \
|
||||
SMPage* _page1 = (SMPage*)_obj1; \
|
||||
SM_CLEAN_PAGE(_page1); \
|
||||
SM_POOL_FREE_LARGE_OBJ(&(gen)->pool, _obj1); \
|
||||
} \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
extern PRUword
|
||||
sm_GenSpaceAvailable(SMGenNum genNum);
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMGEN__ */
|
||||
/******************************************************************************/
|
87
ef/gc/include/smhash.h
Normal file
87
ef/gc/include/smhash.h
Normal file
@ -0,0 +1,87 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMHASH__
|
||||
#define __SMHASH__
|
||||
|
||||
#include "smobj.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
typedef struct SMHashEntry {
|
||||
PRUword key;
|
||||
SMObject* value;
|
||||
} SMHashEntry;
|
||||
|
||||
typedef struct SMHashTable {
|
||||
SMArrayStruct array;
|
||||
SMHashEntry entry[1];
|
||||
/* other hash table entries follow */
|
||||
} SMHashTable;
|
||||
|
||||
#define SM_HASHTABLE_ENTRIES(ht) (&(ht)->entry0)
|
||||
|
||||
#define SM_HASHTABLE_SIZES 32
|
||||
extern PRUword sm_HashPrimes[SM_HASHTABLE_SIZES];
|
||||
extern PRUword sm_HashMasks[SM_HASHTABLE_SIZES];
|
||||
|
||||
#define SM_HASHTABLE_HASH1(ht, k) \
|
||||
(((k) * sm_HashPrimes[ht->inherit.size]) & sm_HashMasks[ht->inherit.size])
|
||||
|
||||
#define SM_HASHTABLE_HASH2(h) \
|
||||
(((h) >> sm_HashMasks[ht->inherit.size - 1]) | 1)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_EXTERN(SMHashTable*)
|
||||
SM_NewHashTable(PRUword initialSize, PRUword maxSize);
|
||||
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_HashTableLookup(SMHashTable* self, PRUword key);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_HashTableInsert(SMHashTable* self, PRUword key, SMObject* value);
|
||||
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_HashTableRemove(SMHashTable* self, PRUword key);
|
||||
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_HashTableDelete(SMHashTable* self, PRUword key);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMHASH__ */
|
||||
/******************************************************************************/
|
327
ef/gc/include/smheap.h
Normal file
327
ef/gc/include/smheap.h
Normal file
@ -0,0 +1,327 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMHEAP__
|
||||
#define __SMHEAP__
|
||||
|
||||
#include "sm.h"
|
||||
#include "smgen.h"
|
||||
#include "smstack.h"
|
||||
#include "prthread.h"
|
||||
#include "prmon.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Object Descriptors
|
||||
******************************************************************************/
|
||||
|
||||
/* object is finalizable -- flag used both inside and outside of gc */
|
||||
|
||||
#define SM_OBJDESC_FINALIZABLE_MASK (1 << SMObjectDescFlag_FinalizableBit)
|
||||
|
||||
#define SM_OBJDESC_IS_FINALIZABLE(od) \
|
||||
((od)->flags & SM_OBJDESC_FINALIZABLE_MASK) \
|
||||
|
||||
#define SM_OBJDESC_SET_FINALIZABLE(od) \
|
||||
(SM_ASSERT(!SM_OBJDESC_IS_FREE(od)), \
|
||||
(od)->flags |= SM_OBJDESC_FINALIZABLE_MASK) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* object is otherwise free but needs finalization */
|
||||
|
||||
#define SM_OBJDESC_NEEDS_FINALIZATION_MASK \
|
||||
(1 << SMObjectDescFlag_NeedsFinalizationBit) \
|
||||
|
||||
#define SM_OBJDESC_NEEDS_FINALIZATION(od) \
|
||||
((od)->flags & SM_OBJDESC_NEEDS_FINALIZATION_MASK) \
|
||||
|
||||
#define SM_OBJDESC_SET_NEEDS_FINALIZATION(od) \
|
||||
((od)->flags |= SM_OBJDESC_NEEDS_FINALIZATION_MASK) \
|
||||
|
||||
#define SM_OBJDESC_CLEAR_FINALIZATION(od) \
|
||||
((od)->flags &= ~(SM_OBJDESC_NEEDS_FINALIZATION_MASK | \
|
||||
SM_OBJDESC_FINALIZABLE_MASK)) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* object was free before we started the gc -- gc-only state */
|
||||
|
||||
#define SM_OBJDESC_WAS_FREE(od) \
|
||||
(((od)->flags & SM_OBJDESC_STATE_MASK) == SMObjectState_WasFree) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* object is pinned -- gc-only flag */
|
||||
|
||||
#define SM_OBJDESC_PINNED_MASK (1 << SMObjectDescFlag_PinnedBit)
|
||||
|
||||
/* we need a version of this without asserts or else we blow out CodeWarrior */
|
||||
#define SM_OBJDESC_IS_PINNED0(od) \
|
||||
((od)->flags & SM_OBJDESC_PINNED_MASK) \
|
||||
|
||||
#define SM_OBJDESC_IS_PINNED(od) \
|
||||
(SM_ASSERT(!SM_OBJDESC_WAS_FREE(od)), \
|
||||
SM_OBJDESC_IS_PINNED0(od)) \
|
||||
|
||||
#define SM_OBJDESC_SET_PINNED(od) \
|
||||
(SM_ASSERT(!SM_OBJDESC_WAS_FREE(od)), \
|
||||
(od)->flags |= SM_OBJDESC_PINNED_MASK) \
|
||||
|
||||
#define SM_OBJDESC_CLEAR_PINNED(od) \
|
||||
(SM_ASSERT(!SM_OBJDESC_WAS_FREE(od)), \
|
||||
(od)->flags &= ~SM_OBJDESC_PINNED_MASK) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* object is marked -- gc-only state */
|
||||
|
||||
#define SM_OBJDESC_IS_MARKED(od) ((od)->flags < 0)
|
||||
|
||||
#define SM_OBJDESC_UNTRACED_OR_UNMARKED_MASK \
|
||||
((1 << SMObjectDescFlag_StateBit2) | \
|
||||
(1 << SMObjectDescFlag_StateBit0)) \
|
||||
|
||||
#define SM_OBJDESC_UNTRACED_OR_UNMARKED(od) \
|
||||
(((od)->flags & SM_OBJDESC_UNTRACED_OR_UNMARKED_MASK) \
|
||||
== SMObjectState_Unmarked) \
|
||||
|
||||
#define SM_OBJDESC_SET_MARKED(od) \
|
||||
(SM_ASSERT(SM_OBJDESC_UNTRACED_OR_UNMARKED(od)), \
|
||||
((od)->flags |= SMObjectState_Marked)) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* done before a gc (works for free objects too) -- gc-only state */
|
||||
|
||||
#define SM_OBJDESC_IS_UNMARKED(od) \
|
||||
(((od)->flags & SM_OBJDESC_STATE_MASK) == SMObjectState_Unmarked) \
|
||||
|
||||
#define SM_OBJDESC_SET_UNMARKED(od) \
|
||||
((od)->flags &= ~(SM_OBJDESC_ALLOCATED_MASK | SM_OBJDESC_PINNED_MASK))\
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* done when the mark stack overflows -- gc-only state */
|
||||
|
||||
#define SM_OBJDESC_IS_UNTRACED(od) \
|
||||
(((od)->flags & SM_OBJDESC_STATE_MASK) == SMObjectState_Untraced) \
|
||||
|
||||
#define SM_OBJDESC_SET_UNTRACED(od) \
|
||||
(SM_ASSERT(SM_OBJDESC_IS_MARKED(od)), \
|
||||
(od)->flags |= SMObjectState_Untraced) \
|
||||
|
||||
#define SM_OBJDESC_CLEAR_UNTRACED(od) \
|
||||
(SM_ASSERT(SM_OBJDESC_IS_UNTRACED(od)), \
|
||||
(od)->flags &= ~(1 << SMObjectDescFlag_StateBit2)) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* done when an object is promoted -- gc-only state */
|
||||
|
||||
#define SM_OBJDESC_IS_FORWARDED(od) \
|
||||
(((od)->flags & SM_OBJDESC_STATE_MASK) == SMObjectState_Forwarded) \
|
||||
|
||||
#define SM_OBJDESC_SET_FORWARDED(od) \
|
||||
(SM_ASSERT(SM_OBJDESC_IS_UNMARKED(od)), \
|
||||
SM_ASSERT(!SM_OBJDESC_IS_PINNED0(od)), \
|
||||
(od)->flags = (PRUint8)SMObjectState_Forwarded) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* done when an object survives a copy cycle -- gc-only state */
|
||||
|
||||
#define SM_OBJDESC_COPYABLE_MASK (1 << SMObjectDescFlag_CopyableBit)
|
||||
|
||||
#define SM_OBJDESC_IS_COPYABLE(od) \
|
||||
(((od)->flags & SM_OBJDESC_COPYABLE_MASK) == SM_OBJDESC_COPYABLE_MASK)\
|
||||
|
||||
#define SM_OBJDESC_SET_COPYABLE(od) \
|
||||
((od)->flags |= SM_OBJDESC_COPYABLE_MASK) \
|
||||
|
||||
#define SM_OBJDESC_SET_COPYABLE_BUT_PINNED(od) \
|
||||
((od)->flags |= (SM_OBJDESC_COPYABLE_MASK | SM_OBJDESC_PINNED_MASK)) \
|
||||
|
||||
/* copyable and not pinned */
|
||||
#define SM_OBJDESC_DO_COPY(od) \
|
||||
(SM_ASSERT(!SM_OBJDESC_WAS_FREE(od)), \
|
||||
((od)->flags & (SM_OBJDESC_PINNED_MASK | SM_OBJDESC_COPYABLE_MASK)) \
|
||||
== SM_OBJDESC_COPYABLE_MASK) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SM_COPY_CYCLE_BITS 1 /* copy every other collect */
|
||||
#else
|
||||
#define SM_COPY_CYCLE_BITS 3 /* copy every 8th collect */
|
||||
#endif
|
||||
#define SM_COPY_CYCLE_MASK ((1 << SM_COPY_CYCLE_BITS) - 1)
|
||||
#define SM_IS_COPY_CYCLE(genNum) \
|
||||
((PRBool)((sm_Heap.gen[genNum].collectCount & SM_COPY_CYCLE_MASK) == 0))
|
||||
|
||||
/*******************************************************************************
|
||||
* Object Operations
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_OBJECT_FORWARDING_ADDR(obj) \
|
||||
((SMObject*)SM_OBJECT_CLASS(obj)) \
|
||||
|
||||
#define SM_OBJECT_SET_FORWARDING_ADDR(obj, newAddr) \
|
||||
(SM_OBJECT_CLASS(obj) = (SMClass*)(SM_ENSURE(SMObject, newAddr))) \
|
||||
|
||||
/*******************************************************************************
|
||||
* Card Descriptors
|
||||
******************************************************************************/
|
||||
|
||||
typedef PRUint8 SMCardDesc;
|
||||
|
||||
#define SM_CARDDESC_IS_DIRTY(cd) ((cd) == 0)
|
||||
#define SM_CARDDESC_SET_GEN(cdAddr, genNum) (*(cdAddr) = SM_GEN_CARDDESC(genNum))
|
||||
#define SM_CARDDESC_INIT(cdAddr) SM_CARDDESC_SET_GEN(cdAddr, SMGenNum_Free)
|
||||
|
||||
/*******************************************************************************
|
||||
* Heap
|
||||
******************************************************************************/
|
||||
|
||||
struct SMHeap {
|
||||
SMGen gen[SM_GEN_COUNT];
|
||||
SMStack markStack;
|
||||
SMMarkRootsProc markRootsProc;
|
||||
void* markRootsClosure;
|
||||
SMGCHookProc beforeGCHook;
|
||||
void* beforeGCClosure;
|
||||
SMGCHookProc afterGCHook;
|
||||
void* afterGCClosure;
|
||||
SMGenNum collectingGenNum;
|
||||
/* finalizer stuff */
|
||||
PRThread* finalizer;
|
||||
PRMonitor* finalizerMon;
|
||||
PRBool keepRunning;
|
||||
#ifndef SM_NO_WRITE_BARRIER
|
||||
SMCardDesc* cardTableMem;
|
||||
PRUword cardTablePageCount;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* So here's the story: I was going to allow multiple heaps to exist
|
||||
simultaneously (not strictly necessary, but it might be a useful feature
|
||||
someday) but I backed it out because of the extra dereferences it introduces
|
||||
in order to get at any of the heap global data. If we ever need it, we can
|
||||
go back and introduce a heap argument to most of the routines, and eliminate
|
||||
this global. We'd also have to make SM_Init return a new heap. */
|
||||
extern SMHeap sm_Heap; /* _the_ global heap */
|
||||
|
||||
/*******************************************************************************
|
||||
* Debug Methods
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef SM_STATS /* define this if you want stat code in the final product */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct SMStatRecord {
|
||||
PRUword amountUsed;
|
||||
PRUword amountFree;
|
||||
PRUword overhead;
|
||||
PRUword collections;
|
||||
/* for figuring internal fragmentation of allocation requests */
|
||||
PRUword totalAllocated;
|
||||
PRUword totalRequested;
|
||||
} SMStatRecord;
|
||||
|
||||
typedef struct SMStats {
|
||||
SMStatRecord total;
|
||||
SMStatRecord perGen[SM_GEN_COUNT + 1];
|
||||
PRUword systemOverhead;
|
||||
} SMStats;
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_Stats(SMStats* stats);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DumpStats(FILE* out, PRBool detailed);
|
||||
|
||||
#endif /* SM_STATS */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_SetCollectThresholds(PRUword gen0Threshold, PRUword gen1Threshold,
|
||||
PRUword gen2Threshold, PRUword gen3Threshold,
|
||||
PRUword staticGenThreshold);
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef SM_VERIFY
|
||||
|
||||
extern void
|
||||
sm_VerifyHeap(void);
|
||||
|
||||
#define SM_VERIFY_HEAP() sm_VerifyHeap()
|
||||
|
||||
#else /* !SM_VERIFY */
|
||||
|
||||
#define SM_VERIFY_HEAP() /* no-op */
|
||||
|
||||
#endif /* !SM_VERIFY */
|
||||
|
||||
/*******************************************************************************
|
||||
* Private Methods
|
||||
******************************************************************************/
|
||||
|
||||
extern PRStatus
|
||||
sm_InitFinalizer(void);
|
||||
|
||||
extern PRStatus
|
||||
sm_FiniFinalizer(PRBool finalizeOnExit);
|
||||
|
||||
extern SMObjStruct*
|
||||
sm_AddGCPage(SMGenNum genNum, SMBucket bucket);
|
||||
|
||||
extern SMObjStruct*
|
||||
sm_Alloc(PRUword instanceSize, SMBucket bucket);
|
||||
|
||||
extern void
|
||||
sm_Collect0(SMGenNum collectingGenNum, PRBool copyCycle);
|
||||
|
||||
extern PRBool
|
||||
sm_CollectAndFinalizeAll(PRUword spaceNeeded);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMHEAP__ */
|
||||
/******************************************************************************/
|
72
ef/gc/include/smmalloc.h
Normal file
72
ef/gc/include/smmalloc.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMMALLOC__
|
||||
#define __SMMALLOC__
|
||||
|
||||
#include "sm.h"
|
||||
#include "smpage.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Malloc Pools
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMPool {
|
||||
SMPageDesc* unfilledPages;
|
||||
SMPageDesc* sweepPage;
|
||||
SMSmallObjCount sweepIndex;
|
||||
PRLock* lock;
|
||||
} SMPool;
|
||||
|
||||
typedef struct SMPool {
|
||||
SMPageMgr pageMgr;
|
||||
PRLock* lock[SM_ALLOC_BUCKETS];
|
||||
struct SMFree* freeList[SM_ALLOC_BUCKETS];
|
||||
} SMPool;
|
||||
|
||||
SM_EXTERN(PRStatus)
|
||||
SM_InitPool(SMPool* pool, SMPageMgr* pageMgr);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_FiniPool(SMPool* pool);
|
||||
|
||||
extern SMSmallObjSize sm_ObjectSize[];
|
||||
extern SMSmallObjCount sm_ObjectsPerPage[];
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMMALLOC__ */
|
||||
/******************************************************************************/
|
355
ef/gc/include/smobj.h
Normal file
355
ef/gc/include/smobj.h
Normal file
@ -0,0 +1,355 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMOBJ__
|
||||
#define __SMOBJ__
|
||||
|
||||
#include "sm.h"
|
||||
#include "smpool.h"
|
||||
#include <stdio.h>
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Instances
|
||||
******************************************************************************/
|
||||
|
||||
struct SMObjStruct {
|
||||
SMClass* clazz;
|
||||
void* subHeader;
|
||||
/* fields follow... */
|
||||
};
|
||||
|
||||
struct SMArrayStruct {
|
||||
SMClass* clazz;
|
||||
void* subHeader;
|
||||
PRUword size;
|
||||
/* elements follow... */
|
||||
};
|
||||
|
||||
#if defined(DEBUG) && !defined(SM_CHECK_PTRS)
|
||||
|
||||
/* so we can see what's going on in the debugger... */
|
||||
|
||||
struct SMObject {
|
||||
SMClass* clazz;
|
||||
void* subHeader;
|
||||
SMObject* field0;
|
||||
SMObject* field1;
|
||||
SMObject* field2;
|
||||
SMObject* field3;
|
||||
};
|
||||
|
||||
struct SMArray {
|
||||
SMClass* clazz;
|
||||
void* subHeader;
|
||||
PRUword size;
|
||||
SMObject* element0;
|
||||
SMObject* element1;
|
||||
SMObject* element2;
|
||||
SMObject* element3;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct SMObject {
|
||||
PRUword encrypted;
|
||||
};
|
||||
|
||||
struct SMArray {
|
||||
PRUword encrypted;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define SM_OBJECT_CLASS(obj) (SM_DECRYPT_OBJECT(obj)->clazz)
|
||||
#define SM_OBJECT_FIELDS(obj) ((SMObject**)SM_DECRYPT_OBJECT(obj))
|
||||
#define SM_OBJECT_EXTRA(obj) \
|
||||
((void*)((char*)SM_DECRYPT_OBJECT(obj) \
|
||||
+ SM_OBJECT_CLASS(obj)->object.data.instanceSize)) \
|
||||
|
||||
#define SM_ARRAY_CLASS(arr) (SM_DECRYPT_ARRAY(arr)->clazz)
|
||||
#define SM_ARRAY_SIZE(arr) (SM_DECRYPT_ARRAY(arr)->size)
|
||||
#define SM_ARRAY_ELEMENTS(arr) \
|
||||
((SMObject**)((char*)SM_DECRYPT_ARRAY(arr) + sizeof(SMArrayStruct))) \
|
||||
|
||||
#ifdef SM_DEBUG_HEADER
|
||||
|
||||
/* Just use the subHeader field for this for now (until we get the
|
||||
new monitors). */
|
||||
#define SM_OBJECT_DEBUG_CODE 0xFADE
|
||||
#define SM_CHECK_DEBUG_HEADER(word) (((PRUword)(word) >> 16) == SM_OBJECT_DEBUG_CODE)
|
||||
#define SM_SET_DEBUG_HEADER(obj, bucket, gen) \
|
||||
(obj->subHeader = \
|
||||
(void*)((SM_OBJECT_DEBUG_CODE << 16) | ((bucket) << 8) | (gen))) \
|
||||
|
||||
#else
|
||||
|
||||
#define SM_CHECK_DEBUG_HEADER(word) 0
|
||||
#define SM_SET_DEBUG_HEADER(obj, bucket, gen) ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Class Objects
|
||||
*
|
||||
* Class objects describe the layout of instances and act as vtables for
|
||||
* method dispatch. They are lower-level than their Java equivalent and only
|
||||
* contain the necessary information for execution, not reflection, etc.
|
||||
*
|
||||
* The "info" field is private to the garbage collector. The "fieldsDescs"
|
||||
* field points to an array of field descriptors (described above) and may be
|
||||
* NULL to indicate that there are no collectable fields in the object.
|
||||
*
|
||||
* Interface methods in Java are implemented by a mechanism external to this
|
||||
* representation.
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMClassStruct {
|
||||
SMObjStruct object;
|
||||
PRUword info;
|
||||
/* class-kind specific fields follow */
|
||||
} SMClassStruct;
|
||||
|
||||
#define SM_CLASS_KIND_BITS 4
|
||||
#define SM_CLASS_KIND_MASK ((1 << SM_CLASS_KIND_BITS) - 1)
|
||||
#define SM_CLASS_KIND(cl) (((SMClassStruct*)(cl))->info & SM_CLASS_KIND_MASK)
|
||||
|
||||
#define SM_CLASS_BUCKET_BITS 4
|
||||
#define SM_CLASS_BUCKET_MASK ((1 << SM_CLASS_KIND_BITS) - 1)
|
||||
#define SM_CLASS_BUCKET(cl) \
|
||||
((SMBucket)((((SMClassStruct*)(cl))->info >> SM_CLASS_KIND_BITS) \
|
||||
& SM_CLASS_BUCKET_MASK)) \
|
||||
|
||||
#define SM_CLASS_SET_BUCKET(cl, b) \
|
||||
(SM_ASSERT((b) <= SM_CLASS_BUCKET_MASK), \
|
||||
((SMClassStruct*)(cl))->info |= ((b) << SM_CLASS_KIND_BITS)) \
|
||||
|
||||
#define SM_CLASS_INIT_INFO(cl, bucket, kind, userData) \
|
||||
(SM_ASSERT((bucket) <= SM_CLASS_BUCKET_MASK), \
|
||||
SM_ASSERT((kind) <= SM_CLASS_KIND_MASK), \
|
||||
(((SMClassStruct*)(cl))->info = \
|
||||
(userData) | ((bucket) << SM_CLASS_KIND_BITS) | (kind))) \
|
||||
|
||||
/* The upper bits of the info field may be freely used by the user program */
|
||||
#define SM_CLASS_USER_INFO_FIRST_BIT (SM_CLASS_KIND_BITS - SM_CLASS_BUCKET_BITS)
|
||||
#define SM_CLASS_USER_INFO_BITS \
|
||||
((sizeof(PRUword) * 8) - SM_CLASS_USER_INFO_FIRST_BIT)
|
||||
#define SM_CLASS_USER_INFO(cl) ((cl)->info >> SM_CLASS_USER_INFO_BITS)
|
||||
|
||||
typedef enum SMClassKind {
|
||||
/* Primitive class kinds: */
|
||||
SMClassKind_VoidClass,
|
||||
SMClassKind_BooleanClass,
|
||||
SMClassKind_UByteClass, /* Waldemar likes this type although it seems out of place */
|
||||
SMClassKind_ByteClass,
|
||||
SMClassKind_CharClass,
|
||||
SMClassKind_ShortClass,
|
||||
SMClassKind_IntClass,
|
||||
SMClassKind_LongClass,
|
||||
SMClassKind_FloatClass,
|
||||
SMClassKind_DoubleClass,
|
||||
/* Structured class kinds: */
|
||||
SMClassKind_ArrayClass,
|
||||
SMClassKind_ObjectClass,
|
||||
SMClassKind_InterfaceClass
|
||||
} SMClassKind;
|
||||
|
||||
#define SM_CLASS_KIND_COUNT (SMClassKind_InterfaceClass + 1)
|
||||
|
||||
extern PRUint8 sm_ClassKindSize[SM_CLASS_KIND_COUNT];
|
||||
|
||||
#define SM_ELEMENT_SIZE(cl) (sm_ClassKindSize[SM_CLASS_KIND(cl)])
|
||||
|
||||
/*******************************************************************************
|
||||
* Field Descriptors
|
||||
*
|
||||
* Classes contain an array of field descriptors, one for each contiguous
|
||||
* group of garbage-collectable fields. Field offsets are relative to the start
|
||||
* of the object (i.e. the class descriptor), and are in units of SMObject*
|
||||
* pointers (not bytes).
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMFieldDesc {
|
||||
PRWord offset; /* number of SMObject* pointers from base */
|
||||
PRUword count; /* number of pointers at offset */
|
||||
} SMFieldDesc;
|
||||
|
||||
/* This data structure describes where to find the beginning of an array
|
||||
of field descriptors relative to the beginning of a class object. It
|
||||
also contains the count of descriptors. */
|
||||
typedef struct SMFieldDescInfo {
|
||||
PRUint16 offset; /* start of field descriptors relative to beginning of class */
|
||||
PRUint16 count; /* number of field descriptors at offset */
|
||||
} SMFieldDescInfo;
|
||||
|
||||
#define SM_CLASS_GET_FIELD_DESCS(cl, fdInfo) \
|
||||
((SMFieldDesc*)((char*)(cl) + (fdInfo)->offset)) \
|
||||
|
||||
/*******************************************************************************
|
||||
* Standard Methods
|
||||
*
|
||||
* These methods exist on all instances, some of which are used by the garbage
|
||||
* collector internally.
|
||||
******************************************************************************/
|
||||
|
||||
typedef void
|
||||
(PR_CALLBACK *SMFinalizeFun)(SMObject* obj);
|
||||
|
||||
/*******************************************************************************
|
||||
* Object Class Objects
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMObjectClassData {
|
||||
SMObject* staticFields; /* an array object */
|
||||
SMFieldDescInfo instFieldDescs;
|
||||
SMFieldDescInfo weakFieldDescs;
|
||||
PRUword instanceSize;
|
||||
SMFinalizeFun finalize;
|
||||
} SMObjectClassData;
|
||||
|
||||
struct SMObjectClass {
|
||||
SMClassStruct inherit;
|
||||
SMObjectClassData data;
|
||||
};
|
||||
|
||||
#define SM_CLASS_INSTANCE_SIZE(cl) (((SMClass*)(cl))->object.data.instanceSize)
|
||||
#define SM_CLASS_FINALIZER(cl) (((SMClass*)(cl))->object.data.finalize)
|
||||
#define SM_CLASS_IS_FINALIZABLE(cl) (SM_CLASS_FINALIZER(cl) != NULL)
|
||||
|
||||
#define SM_CLASS_GET_INST_FIELDS_COUNT(cl) \
|
||||
(((SMClass*)(cl))->object.data.instFieldDescs.count) \
|
||||
|
||||
#define SM_CLASS_GET_INST_FIELD_DESCS(cl) \
|
||||
SM_CLASS_GET_FIELD_DESCS(cl, &(cl)->object.data.instFieldDescs) \
|
||||
|
||||
#define SM_CLASS_GET_WEAK_FIELDS_COUNT(cl) \
|
||||
(((SMClass*)(cl))->object.data.weakFieldDescs.count) \
|
||||
|
||||
#define SM_CLASS_GET_WEAK_FIELD_DESCS(cl) \
|
||||
SM_CLASS_GET_FIELD_DESCS(cl, &(cl)->object.data.weakFieldDescs) \
|
||||
|
||||
/*******************************************************************************
|
||||
* Array Class Objects
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMArrayClassData {
|
||||
SMClass* elementClass; /* same position as staticFields, above */
|
||||
} SMArrayClassData;
|
||||
|
||||
struct SMArrayClass {
|
||||
SMClassStruct inherit;
|
||||
SMArrayClassData data;
|
||||
};
|
||||
|
||||
#define SM_CLASS_ELEMENT_CLASS(cl) (((SMClass*)(cl))->array.data.elementClass)
|
||||
|
||||
/*******************************************************************************
|
||||
* Primitive Class Objects
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMPrimClassData {
|
||||
/* no other fields */
|
||||
char dummy;
|
||||
} SMPrimClassData;
|
||||
|
||||
struct SMPrimClass {
|
||||
SMClassStruct inherit;
|
||||
SMPrimClassData data;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
union SMClass {
|
||||
SMObjectClass object;
|
||||
SMArrayClass array;
|
||||
SMPrimClass prim;
|
||||
};
|
||||
|
||||
/* Class structure excluding portion from SMObject: */
|
||||
typedef struct SMClassData {
|
||||
PRUword info;
|
||||
union {
|
||||
SMObjectClassData object;
|
||||
SMArrayClassData array;
|
||||
SMPrimClassData prim;
|
||||
} type;
|
||||
} SMClassData;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define SM_IS_OBJECT_CLASS(cl) \
|
||||
(SM_CLASS_KIND(cl) > SMClassKind_ArrayClass) \
|
||||
|
||||
#define SM_IS_ARRAY_CLASS(cl) \
|
||||
(SM_CLASS_KIND(cl) == SMClassKind_ArrayClass) \
|
||||
|
||||
#define SM_IS_PRIM_CLASS(cl) \
|
||||
(SM_CLASS_KIND(cl) < SMClassKind_ArrayClass) \
|
||||
|
||||
#define SM_IS_OBJECT_ARRAY_CLASS(cl) \
|
||||
(SM_CLASS_KIND(cl) == SMClassKind_ArrayClass \
|
||||
&& !SM_IS_PRIM_CLASS(SM_CLASS_ELEMENT_CLASS(cl))) \
|
||||
|
||||
#define SM_IS_PRIMITIVE_ARRAY_CLASS(cl) \
|
||||
(SM_CLASS_KIND(cl) == SMClassKind_ArrayClass \
|
||||
&& SM_IS_PRIM_CLASS(SM_CLASS_ELEMENT_CLASS(cl))) \
|
||||
|
||||
#define SM_IS_OBJECT(obj) \
|
||||
(SM_IS_OBJECT_CLASS(SM_OBJECT_CLASS(obj))) \
|
||||
|
||||
#define SM_IS_ARRAY(obj) \
|
||||
(SM_IS_ARRAY_CLASS(SM_OBJECT_CLASS(obj))) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_InitObjectClass(SMObjectClass* clazz, SMObjectClass* metaClass,
|
||||
PRUword classSize, PRUword instanceSize,
|
||||
PRUint16 nInstFieldDescs, PRUint16 nWeakFieldDescs);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_InitArrayClass(SMArrayClass* clazz, SMObjectClass* metaClass,
|
||||
SMClass* elementClass);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_InitPrimClass(SMPrimClass* clazz, SMObjectClass* metaClass,
|
||||
SMClassKind primKind);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMOBJ__ */
|
||||
/******************************************************************************/
|
282
ef/gc/include/smpage.h
Normal file
282
ef/gc/include/smpage.h
Normal file
@ -0,0 +1,282 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMPAGE__
|
||||
#define __SMPAGE__
|
||||
|
||||
#include "sm.h"
|
||||
#include "prmon.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_PAGE_BITS 12 /* 4k pages */
|
||||
#define SM_PAGE_SIZE (1 << SM_PAGE_BITS)
|
||||
#define SM_PAGE_MASK (SM_PAGE_SIZE - 1)
|
||||
#define SM_PAGE_REFS (SM_PAGE_SIZE / sizeof(void*))
|
||||
|
||||
/* (1 << SM_REF_BITS) == sizeof(void*) */
|
||||
#if defined(XP_WIN) && !defined(_WIN32)
|
||||
#define SM_REF_BITS 1
|
||||
#elif defined(IS_64)
|
||||
#define SM_REF_BITS 3
|
||||
#else
|
||||
#define SM_REF_BITS 2
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Types
|
||||
******************************************************************************/
|
||||
|
||||
typedef PRUint8 SMPage[SM_PAGE_SIZE];
|
||||
typedef PRUword SMPageCount; /* int big enough to count pages */
|
||||
|
||||
/* SMSmallObjCount: int big enough to count objects per page */
|
||||
#if SM_PAGE_BITS <= 8
|
||||
typedef PRUint8 SMSmallObjCount;
|
||||
#elif SM_PAGE_BITS <= 16
|
||||
typedef PRUint16 SMSmallObjCount;
|
||||
#else
|
||||
typedef PRUint32 SMSmallObjCount;
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Macros
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_PAGE_NUMBER(addr) ((SMPageCount)(addr) >> SM_PAGE_BITS)
|
||||
#define SM_PAGE_ADDR(num) ((SMPage*)((SMPageCount)(num) << SM_PAGE_BITS))
|
||||
#define SM_PAGE_OFFSET(addr) ((PRUword)(addr) & SM_PAGE_MASK)
|
||||
#define SM_PAGE_REFOFFSET(addr) (SM_PAGE_OFFSET(addr) >> SM_REF_BITS)
|
||||
#define SM_PAGE_BASE(addr) ((SMPage*)((PRUword)(addr) & ~SM_PAGE_MASK))
|
||||
#define SM_PAGE_COUNT(size) SM_PAGE_NUMBER((size) + SM_PAGE_MASK)
|
||||
#define SM_PAGE_WIDTH(count) ((PRUword)((SMPageCount)(count) << SM_PAGE_BITS))
|
||||
|
||||
#define SM_PAGE_ROUNDDN(addr) ((SMPage*)SM_ALIGN((PRUword)(addr), SM_PAGE_BITS))
|
||||
#define SM_PAGE_ROUNDUP(addr) SM_PAGE_ROUNDDN((PRUword)(addr) + SM_PAGE_SIZE)
|
||||
|
||||
/*******************************************************************************
|
||||
* Object Descriptors
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMObjDesc {
|
||||
PRInt8 flags; /* must be signed */
|
||||
} SMObjDesc;
|
||||
|
||||
typedef enum SMObjectDescFlag {
|
||||
SMObjectDescFlag_FinalizableBit,
|
||||
SMObjectDescFlag_NeedsFinalizationBit,
|
||||
SMObjectDescFlag_PinnedBit,
|
||||
SMObjectDescFlag_CopyableBit,
|
||||
|
||||
SMObjectDescFlag_Unused0,
|
||||
SMObjectDescFlag_StateBit0,
|
||||
SMObjectDescFlag_StateBit1,
|
||||
SMObjectDescFlag_StateBit2 /* high bit -- can use test for negative */
|
||||
} SMObjectDescFlag;
|
||||
|
||||
/* Be careful about changing these SMObjectState values. The macros below
|
||||
are highly dependent on them. */
|
||||
typedef enum SMObjectState {
|
||||
SMObjectState_WasFree = 0x00, /* 00000000 */
|
||||
SMObjectState_Unmarked = 0x20, /* 00100000 */
|
||||
SMObjectState_Untraced = 0x60, /* 01100000 */
|
||||
SMObjectState_Forwarded = 0x80, /* 10000000 */
|
||||
SMObjectState_Marked = 0xE0 /* 11100000 */
|
||||
} SMObjectState;
|
||||
|
||||
#define SM_OBJDESC_STATE_MASK ((1 << SMObjectDescFlag_StateBit2) | \
|
||||
(1 << SMObjectDescFlag_StateBit1) | \
|
||||
(1 << SMObjectDescFlag_StateBit0))
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* (space for) object is really free -- non-gc state */
|
||||
|
||||
#define SM_OBJDESC_FREE_MASK (1 << SMObjectDescFlag_StateBit1)
|
||||
|
||||
#define SM_OBJDESC_IS_FREE(od) \
|
||||
(((od)->flags & SM_OBJDESC_FREE_MASK) == 0) \
|
||||
|
||||
#define SM_OBJDESC_SET_FREE(od) ((od)->flags = 0)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* object is allocated -- non-gc state */
|
||||
|
||||
#define SM_OBJDESC_ALLOCATED_MASK ((1 << SMObjectDescFlag_StateBit2) | \
|
||||
(1 << SMObjectDescFlag_StateBit1))
|
||||
|
||||
#define SM_OBJDESC_IS_ALLOCATED(od) \
|
||||
(((od)->flags & SM_OBJDESC_ALLOCATED_MASK) \
|
||||
== SM_OBJDESC_ALLOCATED_MASK) \
|
||||
|
||||
#define SM_OBJDESC_SET_ALLOCATED(od) \
|
||||
(SM_ASSERT(SM_OBJDESC_IS_FREE(od)), \
|
||||
((od)->flags |= SMObjectState_Marked)) \
|
||||
|
||||
/*******************************************************************************
|
||||
* Page Descriptors
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMPageDesc SMPageDesc;
|
||||
|
||||
struct SMPageDesc {
|
||||
SMPageDesc* next;
|
||||
SMObjDesc* objTable;
|
||||
SMSmallObjCount allocCount;
|
||||
PRUint8 flags;
|
||||
SMObjDesc largeObjDesc;
|
||||
};
|
||||
|
||||
typedef enum SMPageFlag {
|
||||
SMPageFlag_GenBit0,
|
||||
SMPageFlag_GenBit1,
|
||||
SMPageFlag_GenBit2,
|
||||
SMPageFlag_BlackListedBit,
|
||||
SMPageFlag_BucketBit0,
|
||||
SMPageFlag_BucketBit1,
|
||||
SMPageFlag_BucketBit2,
|
||||
SMPageFlag_BucketBit3
|
||||
} SMPageFlag;
|
||||
|
||||
#define SM_PAGEDESC_GEN_BITS (SMPageFlag_GenBit2 + 1)
|
||||
#define SM_PAGEDESC_GEN_MASK ((1 << SM_PAGEDESC_GEN_BITS) - 1)
|
||||
#define SM_PAGEDESC_GEN(pd) ((SMGenNum)((pd)->flags & SM_PAGEDESC_GEN_MASK))
|
||||
#define SM_PAGEDESC_INCR_GEN(pd) ((pd)->flags += 1)
|
||||
#define SM_PAGEDESC_BUCKET(pd) ((pd)->flags >> SMPageFlag_BucketBit0)
|
||||
|
||||
#define SM_PAGEDESC_BLACKLISTED_MASK (1 << SMPageFlag_BlackListedBit)
|
||||
#define SM_PAGEDESC_IS_BLACKLISTED(pd) ((pd)->flags & SM_PAGEDESC_BLACKLISTED_MASK)
|
||||
#define SM_PAGEDESC_SET_BLACKLISTED(pd) ((pd)->flags |= SM_PAGEDESC_BLACKLISTED_MASK)
|
||||
#define SM_PAGEDESC_CLEAR_BLACKLISTED(pd) ((pd)->flags &= ~SM_PAGEDESC_BLACKLISTED_MASK)
|
||||
|
||||
#define SM_PAGEDESC_INIT(pd, bucket, genNum, objTab, allocCnt) \
|
||||
((pd)->next = NULL, \
|
||||
(pd)->objTable = (objTab), \
|
||||
(pd)->allocCount = (allocCnt), \
|
||||
(pd)->flags = ((bucket) << SMPageFlag_BucketBit0) | (genNum), \
|
||||
(pd)->largeObjDesc.flags = 0) \
|
||||
|
||||
#define SM_PAGEDESC_BUCKET_BITS 4
|
||||
#define SM_PAGEDESC_BUCKET_COUNT (1 << SM_PAGEDESC_BUCKET_BITS)
|
||||
#define SM_PAGEDESC_PAGENUM(pd) (pd - sm_PageMgr.pageTable)
|
||||
#define SM_PAGEDESC_PAGE(pd) SM_PAGE_ADDR(SM_PAGEDESC_PAGENUM(pd))
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define SM_PAGEDESC_IS_LARGE_OBJECT_START(pd) ((pd)->allocCount != 0)
|
||||
|
||||
/* The following only works if pd is not the start of a large object: */
|
||||
#define SM_PAGEDESC_LARGE_OBJECT_START(pd) ((pd)->next)
|
||||
|
||||
/*******************************************************************************
|
||||
* Page Manager
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMClusterDesc SMClusterDesc;
|
||||
typedef struct SMSegmentDesc SMSegmentDesc;
|
||||
|
||||
typedef struct SMPageMgr {
|
||||
SMClusterDesc* freeClusters;
|
||||
PRMonitor* monitor;
|
||||
SMPage* memoryBase;
|
||||
SMPage* boundary;
|
||||
SMPageCount minPage;
|
||||
SMPageCount pageCount;
|
||||
PRBool alreadyLocked;
|
||||
#if defined(XP_MAC)
|
||||
PRUint8* segMap;
|
||||
SMSegmentDesc* segTable;
|
||||
PRWord segTableCount;
|
||||
#endif
|
||||
|
||||
/* the page table */
|
||||
SMPageDesc* pageTableMem;
|
||||
SMPageDesc* pageTable; /* one per page */
|
||||
SMPage* heapBase;
|
||||
PRUword heapPageCount;
|
||||
} SMPageMgr;
|
||||
|
||||
#define SM_PAGEMGR_IN_RANGE(pm, ptr) \
|
||||
((void*)(pm)->heapBase <= (ptr) && (ptr) < (void*)(pm)->boundary)
|
||||
|
||||
/* So here's the story: I was going to allow multiple heaps to exist
|
||||
simultaneously (not strictly necessary, but it might be a useful feature
|
||||
someday) but I backed it out because of the extra dereferences it introduces
|
||||
in order to get at any of the heap global data. If we ever need it, we can
|
||||
go back and introduce a heap argument to most of the routines, and eliminate
|
||||
this global. We'd also have to make SM_Init return a new heap. */
|
||||
extern SMPageMgr sm_PageMgr; /* _the_ global page manager */
|
||||
|
||||
/*******************************************************************************
|
||||
* Object Operations
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_OBJECT_PAGEDESC(obj) \
|
||||
(&sm_PageMgr.pageTable[SM_PAGE_NUMBER(obj)]) \
|
||||
|
||||
#define SM_OBJECT_HEADER_FROM_PAGEDESC(obj, pageDesc) \
|
||||
(&(pageDesc)->objTable[SM_DIV((SMSmallObjCount)SM_PAGE_OFFSET(obj), \
|
||||
SM_PAGEDESC_BUCKET(pageDesc))]) \
|
||||
|
||||
#define SM_IN_HEAP(obj) \
|
||||
SM_PAGEMGR_IN_RANGE(&sm_PageMgr, (void*)SM_ENSURE(SMObjStruct, obj)) \
|
||||
|
||||
/*******************************************************************************
|
||||
* Functions
|
||||
******************************************************************************/
|
||||
|
||||
extern PRStatus
|
||||
sm_InitPageMgr(SMPageMgr* pm, SMPageCount minPages, SMPageCount maxPages);
|
||||
|
||||
extern void
|
||||
sm_FiniPageMgr(SMPageMgr* pm);
|
||||
|
||||
extern SMPage*
|
||||
sm_NewCluster(SMPageMgr* pm, SMPageCount nPages);
|
||||
|
||||
extern void
|
||||
sm_DestroyCluster(SMPageMgr* pm, SMPage* basePage, SMPageCount nPages);
|
||||
|
||||
#define SM_NEW_PAGE(pm) sm_NewCluster((pm), 1)
|
||||
#define SM_DESTROY_PAGE(pm, page) sm_DestroyCluster((pm), (page), 1)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMPAGE__ */
|
||||
/******************************************************************************/
|
320
ef/gc/include/smpool.h
Normal file
320
ef/gc/include/smpool.h
Normal file
@ -0,0 +1,320 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMPOOL__
|
||||
#define __SMPOOL__
|
||||
|
||||
#include "sm.h"
|
||||
#include "smpage.h"
|
||||
#include "prmon.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Allocation
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_MIN_SMALLOBJECT_BITS 3 /* 8 bytes */
|
||||
#define SM_MIN_SMALLOBJECT_SIZE (1 << SM_MIN_SMALLOBJECT_BITS)
|
||||
#define SM_MAX_SMALLOBJECT_DENOM_BITS 2 /* one forth of a page */
|
||||
#define SM_MAX_SMALLOBJECT_BITS (SM_PAGE_BITS - SM_MAX_SMALLOBJECT_DENOM_BITS)
|
||||
#define SM_MAX_SMALLOBJECT_SIZE (1 << SM_MAX_SMALLOBJECT_BITS)
|
||||
|
||||
/* Number of allocation buckets needed for SM_MIN_SMALLOBJECT_SIZE <=
|
||||
size <= SM_MAX_SMALLOBJECT_SIZE. The value is really:
|
||||
ln(SM_MAX_SMALLOBJECT_SIZE) - ln(SM_MIN_SMALLOBJECT_SIZE) + 1
|
||||
== SM_MAX_SMALLOBJECT_BITS - SM_MIN_SMALLOBJECT_BITS + 1
|
||||
Be careful to keep these all in sync. */
|
||||
#define SM_POWER_OF_TWO_BUCKETS \
|
||||
(SM_MAX_SMALLOBJECT_BITS - SM_MIN_SMALLOBJECT_BITS + 1)
|
||||
|
||||
#define SM_MIDSIZE_BUCKETS (SM_POWER_OF_TWO_BUCKETS - 1)
|
||||
#define SM_FIRST_MIDSIZE_BUCKET SM_POWER_OF_TWO_BUCKETS
|
||||
#define SM_LARGE_OBJECT_BUCKET (SM_POWER_OF_TWO_BUCKETS + SM_MIDSIZE_BUCKETS)
|
||||
#define SM_ALLOC_BUCKETS (SM_LARGE_OBJECT_BUCKET + 1)
|
||||
|
||||
typedef PRUint8 SMBucket;
|
||||
|
||||
#ifdef IS_64
|
||||
#error "fix me"
|
||||
#else
|
||||
|
||||
/* Find the bin number for a given size (in bytes). This rounds
|
||||
up as values from (2^n)+1 to (2^(n+1)) share the same bin. */
|
||||
#define SM_SIZE_BIN(bin, size) \
|
||||
{ \
|
||||
PRUword _t, _n = (PRUword)(size); \
|
||||
bin = 0; \
|
||||
if ((_t = (_n >> 16)) != 0) { bin += 16; _n = _t; } \
|
||||
if ((_t = (_n >> 8)) != 0) { bin += 8; _n = _t; } \
|
||||
if ((_t = (_n >> 4)) != 0) { bin += 4; _n = _t; } \
|
||||
if ((_t = (_n >> 2)) != 0) { bin += 2; _n = _t; } \
|
||||
if ((_t = (_n >> 1)) != 0) { bin += 1; _n = _t; } \
|
||||
if (_n != 0) bin++; \
|
||||
} \
|
||||
|
||||
/* Returns the bucket for a given size. */
|
||||
#define SM_GET_ALLOC_BUCKET(bucket, size) \
|
||||
{ \
|
||||
PRUword _mid, _size = (PRUword)(size); \
|
||||
if (_size <= SM_MIN_SMALLOBJECT_SIZE) \
|
||||
bucket = 0; \
|
||||
else if (_size > SM_MAX_SMALLOBJECT_SIZE) \
|
||||
bucket = SM_LARGE_OBJECT_BUCKET; \
|
||||
else { \
|
||||
SM_SIZE_BIN(bucket, _size - 1); \
|
||||
_mid = (PRUword)(3 << (bucket - 2)); \
|
||||
if (_size <= _mid) { \
|
||||
bucket += SM_FIRST_MIDSIZE_BUCKET - 1; \
|
||||
} \
|
||||
bucket -= SM_MIN_SMALLOBJECT_BITS; \
|
||||
} \
|
||||
} \
|
||||
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Object Sizes and Counts
|
||||
******************************************************************************/
|
||||
|
||||
/* Smallest type able to hold the size of a small object. Must be kept in
|
||||
sync with SM_MAX_SMALLOBJECT_SIZE, above. */
|
||||
#if SM_MAX_SMALLOBJECT_SIZE <= 256
|
||||
typedef PRUint8 SMSmallObjSize;
|
||||
#elif SM_MAX_SMALLOBJECT_SIZE <= 65536
|
||||
typedef PRUint16 SMSmallObjSize;
|
||||
#else
|
||||
typedef PRUint32 SMSmallObjSize;
|
||||
#endif
|
||||
|
||||
extern SMSmallObjSize sm_ObjectSize[];
|
||||
extern SMSmallObjCount sm_ObjectsPerPage[];
|
||||
|
||||
#define SM_OBJECT_GROSS_SIZE(sizeResult, obj) \
|
||||
{ \
|
||||
SMPageCount _pageNum = SM_PAGE_NUMBER(obj); \
|
||||
SMPageDesc* _pd = &sm_PageMgr.pageTable[_pageNum]; \
|
||||
SMBucket _bucket = SM_PAGEDESC_BUCKET(_pd); \
|
||||
if (_bucket == SM_LARGE_OBJECT_BUCKET) { \
|
||||
SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd)); \
|
||||
*(sizeResult) = SM_PAGE_WIDTH(_pd->allocCount); \
|
||||
} \
|
||||
else { \
|
||||
SMBucket _bucket = SM_PAGEDESC_BUCKET(_pd); \
|
||||
*(sizeResult) = sm_ObjectSize[_bucket]; \
|
||||
} \
|
||||
} \
|
||||
|
||||
/*******************************************************************************
|
||||
* Fast Division
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
|
||||
/* Without SM_NO_TABLE_DIVISION defined, division is done by table lookup
|
||||
which looks the fastest. However it takes up the space of the table, and
|
||||
could contribute to higher level-two cache miss rates for large working
|
||||
sets (we need to take some real world measurements). */
|
||||
|
||||
#define SM_DIVTABLE_ENTRY_COUNT (SM_MIDSIZE_BUCKETS * SM_PAGE_REFS + 1)
|
||||
|
||||
extern SMSmallObjCount* sm_ObjectSizeDivTable[SM_ALLOC_BUCKETS];
|
||||
|
||||
#define SM_DIV(offset, bucket) \
|
||||
(((bucket) == SM_LARGE_OBJECT_BUCKET) \
|
||||
? 0 \
|
||||
: (((bucket) < SM_FIRST_MIDSIZE_BUCKET) \
|
||||
? ((offset) >> ((bucket) + SM_MIN_SMALLOBJECT_BITS)) \
|
||||
: sm_ObjectSizeDivTable[bucket][(offset) >> SM_REF_BITS])) \
|
||||
|
||||
#else /* SM_NO_TABLE_DIVISION */
|
||||
|
||||
/* With SM_NO_TABLE_DIVISION defined, division is done by a fancy fixed-point
|
||||
algorithm that's hard-coded to divide by 3/2 times some power of 2. (Ask
|
||||
Waldemar if you want to know how it works.) It looks like it may be a little
|
||||
slower (although we have to measure it in the real-world) but it takes
|
||||
less space because there's no division table. */
|
||||
|
||||
#define SM_DIV_3_2(offset, bucket) \
|
||||
((((offset) + 1) * 21845) \
|
||||
>> ((bucket) - SM_FIRST_MIDSIZE_BUCKET+ 18)) \
|
||||
|
||||
#define SM_DIV(offset, bucket) \
|
||||
(((bucket) == SM_LARGE_OBJECT_BUCKET) \
|
||||
? 0 \
|
||||
: (((bucket) < SM_FIRST_MIDSIZE_BUCKET) \
|
||||
? ((offset) >> ((bucket) + SM_MIN_SMALLOBJECT_BITS)) \
|
||||
: SM_DIV_3_2(offset, bucket))) \
|
||||
|
||||
#endif /* SM_NO_TABLE_DIVISION */
|
||||
|
||||
/*******************************************************************************
|
||||
* Pools
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct SMSweepList {
|
||||
SMPageDesc* sweepPage;
|
||||
SMSmallObjCount sweepIndex;
|
||||
PRMonitor* monitor;
|
||||
} SMSweepList;
|
||||
|
||||
struct SMPool {
|
||||
SMSweepList sweepList[SM_ALLOC_BUCKETS];
|
||||
PRUword allocAmount;
|
||||
#ifdef SM_STATS
|
||||
PRUword totalAllocated;
|
||||
PRUword totalRequested;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef SM_STATS
|
||||
|
||||
#define SM_POOL_SET_ALLOC_STATS(pool, allocated, requested) \
|
||||
{ \
|
||||
PRUword _alloc = (pool)->totalAllocated; \
|
||||
(pool)->totalAllocated += (allocated); \
|
||||
(pool)->totalRequested += (requested); \
|
||||
SM_ASSERT(_alloc <= (pool)->totalAllocated); \
|
||||
} \
|
||||
|
||||
#else
|
||||
|
||||
#define SM_POOL_SET_ALLOC_STATS(pool, allocated, requested)
|
||||
|
||||
#endif
|
||||
|
||||
#define SM_POOL_DESTROY_PAGE(pool, pd) \
|
||||
{ \
|
||||
SMPageDesc* _pd = (pd); \
|
||||
SMPageCount _pageNum = _pd - sm_PageMgr.pageTable; \
|
||||
SMPage* _page = SM_PAGE_ADDR(_pageNum); \
|
||||
SMObjDesc* _objTable = _pd->objTable; \
|
||||
if (_objTable) \
|
||||
SM_Free(_objTable); \
|
||||
SM_DESTROY_PAGE(&sm_PageMgr, _page); \
|
||||
} \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
extern void
|
||||
sm_InitAntiBuckets(void);
|
||||
|
||||
extern void
|
||||
sm_InitFastDivision(void);
|
||||
|
||||
extern void*
|
||||
sm_PoolInitPage(SMPool* pool, SMGenNum genNum, SMBucket bucket,
|
||||
SMPage* page, SMObjDesc* objTable, SMSmallObjCount allocIndex);
|
||||
|
||||
extern PRStatus
|
||||
sm_InitPool(SMPool* pool);
|
||||
|
||||
extern void
|
||||
sm_FiniPool(SMPool* pool);
|
||||
|
||||
extern void*
|
||||
sm_PoolAllocObj(SMPool* pool, SMBucket bucket);
|
||||
|
||||
#define SM_POOL_FREE_OBJ(pool, obj) \
|
||||
{ \
|
||||
SMPool* _pool = (pool); \
|
||||
void* _obj = (obj); \
|
||||
SMPageCount _pageNum = SM_PAGE_NUMBER(_obj); \
|
||||
SMSmallObjCount _objOffset = (SMSmallObjCount)SM_PAGE_OFFSET(_obj); \
|
||||
SMPageDesc* _pd = &sm_PageMgr.pageTable[_pageNum]; \
|
||||
SMBucket _bucket = SM_PAGEDESC_BUCKET(_pd); \
|
||||
SMSmallObjSize _objIndex = SM_DIV(_objOffset, _bucket); \
|
||||
SMObjDesc* _od = &_pd->objTable[_objIndex]; \
|
||||
SMSmallObjCount _objsPerPage = sm_ObjectsPerPage[_bucket]; \
|
||||
SMSweepList* _sweepList = &_pool->sweepList[bucket]; \
|
||||
SM_ASSERT(!SM_OBJDESC_IS_FREE(_od)); \
|
||||
SM_OBJDESC_SET_FREE(_od); \
|
||||
if (_pd->allocCount-- == _objsPerPage) { \
|
||||
/* page was full -- add it to the front of the sweep list */ \
|
||||
SM_ASSERT(_pd->next == NULL); \
|
||||
_pd->next = _sweepList->sweepPage; \
|
||||
_sweepList->sweepPage = _pd; \
|
||||
_sweepList->sweepIndex = _objIndex; \
|
||||
} \
|
||||
else if (_pd == _sweepList->sweepPage \
|
||||
&& _objIndex < _sweepList->sweepIndex) { \
|
||||
/* if the object was on the first sweep page, and before */ \
|
||||
/* the sweep index, move the sweep index back */ \
|
||||
_sweepList->sweepIndex = _objIndex; \
|
||||
} \
|
||||
else if (_pd->allocCount == 0) { \
|
||||
sm_PoolRemovePage(_pool, _bucket, _pd); \
|
||||
} \
|
||||
SM_POOL_VERIFY_PAGE(_pool, _bucket, _pd); \
|
||||
} \
|
||||
|
||||
/* used by malloc, not the gc */
|
||||
extern void
|
||||
sm_PoolRemovePage(SMPool* pool, SMBucket bucket, SMPageDesc* freePd);
|
||||
|
||||
extern void*
|
||||
sm_PoolAllocLargeObj(SMPool* pool, SMGenNum genNum, PRUword size);
|
||||
|
||||
#define SM_POOL_FREE_LARGE_OBJ(pool, obj) \
|
||||
{ \
|
||||
SMPage* _page = (SMPage*)(obj); \
|
||||
SMPageDesc* _pd = SM_OBJECT_PAGEDESC(_page); \
|
||||
SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd)); \
|
||||
sm_DestroyCluster(&sm_PageMgr, _page, _pd->allocCount); \
|
||||
} \
|
||||
|
||||
typedef PRBool
|
||||
(PR_CALLBACK* SMCompactProc)(PRUword spaceNeeded);
|
||||
|
||||
extern SMCompactProc sm_CompactHook;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
extern void
|
||||
sm_PoolVerifyPage(SMPool* pool, SMBucket bucket, SMPageDesc* pd);
|
||||
|
||||
#define SM_POOL_VERIFY_PAGE(pool, bucket, pd) sm_PoolVerifyPage(pool, bucket, pd)
|
||||
|
||||
#else /* !DEBUG */
|
||||
|
||||
#define SM_POOL_VERIFY_PAGE(pool, bucket, pd) /* no-op */
|
||||
|
||||
#endif /* !DEBUG */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMPOOL__ */
|
||||
/******************************************************************************/
|
216
ef/gc/include/smpriv.h
Normal file
216
ef/gc/include/smpriv.h
Normal file
@ -0,0 +1,216 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMPRIV__
|
||||
#define __SMPRIV__
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "prlog.h" /* for PR_ASSERT */
|
||||
#ifdef XP_MAC
|
||||
#include "pprthred.h" /* for PR_InMonitor */
|
||||
#else
|
||||
#include "private/pprthred.h"
|
||||
#endif
|
||||
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
/*******************************************************************************
|
||||
* Build Settings
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifndef SM_STATS
|
||||
#define SM_STATS
|
||||
#endif
|
||||
#ifndef SM_DUMP
|
||||
#define SM_DUMP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Macros
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_BEGIN_EXTERN_C PR_BEGIN_EXTERN_C
|
||||
#define SM_END_EXTERN_C PR_END_EXTERN_C
|
||||
#define SM_EXTERN(ResultType) PR_EXTERN(ResultType)
|
||||
#define SM_IMPLEMENT(ResultType) PR_IMPLEMENT(ResultType)
|
||||
#define SM_MAX_VALUE(Type) ((1 << (sizeof(Type) * 8)) - 1)
|
||||
|
||||
#ifdef DEBUG
|
||||
SM_EXTERN(void) SM_Assert(const char* message, const char* file, int line);
|
||||
#define SM_ASSERT(test) ((test) ? ((void)0) : SM_Assert(#test , __FILE__, __LINE__))
|
||||
#else
|
||||
#define SM_ASSERT(test) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Use this "ensure" stuff to verify type-correctness of macros. It all
|
||||
becomes a no-op in non-debug mode. */
|
||||
#ifdef DEBUG
|
||||
#define SM_DECLARE_ENSURE(Type) SM_EXTERN(Type*) sm_Ensure##Type(Type* x);
|
||||
#define SM_IMPLEMENT_ENSURE(Type) SM_IMPLEMENT(Type*) sm_Ensure##Type(Type* x) { return x; }
|
||||
#define SM_ENSURE(Type, x) sm_Ensure##Type(x)
|
||||
#else
|
||||
#define SM_DECLARE_ENSURE(Type) /* no-op */
|
||||
#define SM_IMPLEMENT_ENSURE(Type) /* no-op */
|
||||
#define SM_ENSURE(Type, x) (x)
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Pointer Encryption
|
||||
*
|
||||
* These special pointer manipulation routines are used in attempt to guard
|
||||
* against unsafe pointer assigments -- failure to go through the write barrier.
|
||||
* To guarantee that assignments go through the write barrier, we encrypt all
|
||||
* pointers that live in the heap, so that attempts to use them without these
|
||||
* macros will fail.
|
||||
*
|
||||
* These macros are private and should never be used directly by a user program.
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef SM_CHECK_PTRS
|
||||
|
||||
#define SM_POINTER_KEY 0xFADEDEAD /* must be odd */
|
||||
#define SM_ENCRYPT(addr) ((addr) ? ((PRUword)(((PRUword)(addr)) ^ SM_POINTER_KEY)) : (PRUword)0)
|
||||
#define SM_DECRYPT(addr) ((addr) ? ((void*)(((PRUword)(addr)) ^ SM_POINTER_KEY)) : NULL)
|
||||
|
||||
#else /* !SM_CHECK_PTRS */
|
||||
|
||||
#define SM_ENCRYPT(addr) ((PRUword)(addr))
|
||||
#define SM_DECRYPT(addr) ((void*)(addr))
|
||||
|
||||
#endif /* !SM_CHECK_PTRS */
|
||||
|
||||
/*******************************************************************************
|
||||
* Alignment Macros
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_ALIGN(p, nBits) ((PRWord)(p) & ~((1 << nBits) - 1))
|
||||
#define SM_IS_ALIGNED(p, nBits) ((PRWord)(p) == SM_ALIGN(p, nBits))
|
||||
|
||||
#ifdef IS_64
|
||||
#define SM_POINTER_ALIGNMENT 4
|
||||
#else
|
||||
#define SM_POINTER_ALIGNMENT 2
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#include <string.h> /* for memset */
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
extern FILE* sm_DebugOutput;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define DBG_MEMSET(dest, pattern, size) memset(dest, pattern, size)
|
||||
|
||||
#define SM_PAGE_ALLOC_PATTERN 0xCB
|
||||
#define SM_PAGE_FREE_PATTERN 0xCD
|
||||
#define SM_UNUSED_PATTERN 0xDD
|
||||
#define SM_MALLOC_PATTERN 0xEB
|
||||
#define SM_FREE_PATTERN 0xED
|
||||
#define SM_GC_FREE_PATTERN 0xFD
|
||||
|
||||
#else
|
||||
|
||||
#define DBG_MEMSET(dest, pattern, size) ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* "Unrolled" while loops for minimizing loop overhead
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_UNROLL_WHILE
|
||||
#ifdef SM_UNROLL_WHILE
|
||||
|
||||
#define SM_UNROLLED_WHILE(count, body) \
|
||||
{ \
|
||||
PRUword _cnt = (PRUword)(count); \
|
||||
int _rem = _cnt & 7; \
|
||||
_cnt -= _rem; \
|
||||
/* First time through, use switch to jump */ \
|
||||
/* right into the middle of the while loop. */ \
|
||||
switch (_rem) { \
|
||||
while (_cnt > 0) { \
|
||||
_cnt -= 8; \
|
||||
body; \
|
||||
case 7: \
|
||||
body; \
|
||||
case 6: \
|
||||
body; \
|
||||
case 5: \
|
||||
body; \
|
||||
case 4: \
|
||||
body; \
|
||||
case 3: \
|
||||
body; \
|
||||
case 2: \
|
||||
body; \
|
||||
case 1: \
|
||||
body; \
|
||||
case 0: \
|
||||
; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
||||
#else
|
||||
|
||||
#define SM_UNROLLED_WHILE(count, body) \
|
||||
{ \
|
||||
PRUword _cnt = (PRUword)(count); \
|
||||
while (_cnt-- > 0) { \
|
||||
body; \
|
||||
} \
|
||||
} \
|
||||
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Test for overlapping (one-dimensional) regions
|
||||
******************************************************************************/
|
||||
|
||||
#define SM_OVERLAPPING(min1, max1, min2, max2) \
|
||||
(SM_ASSERT((min1) < (max1)), \
|
||||
SM_ASSERT((min2) < (max2)), \
|
||||
((min1) < (max2) && (max1) > (min2))) \
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
#endif /* __SMPRIV__ */
|
||||
/******************************************************************************/
|
112
ef/gc/include/smstack.h
Normal file
112
ef/gc/include/smstack.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMSTACK__
|
||||
#define __SMSTACK__
|
||||
|
||||
#include "smobj.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
typedef struct SMStackSegmentHeader {
|
||||
struct SMStackSegment* prev;
|
||||
struct SMStackSegment* next;
|
||||
} SMStackSegmentHeader;
|
||||
|
||||
typedef struct SMStackSegment {
|
||||
SMStackSegmentHeader header;
|
||||
SMObject* element;
|
||||
/* multiple elements follow */
|
||||
} SMStackSegment;
|
||||
|
||||
#define SM_STACK_SEG_PAGES 1
|
||||
|
||||
typedef struct SMStack {
|
||||
SMStackSegment* segment;
|
||||
SMObject** top;
|
||||
SMObject** min;
|
||||
SMObject** max;
|
||||
PRUword overflowCount;
|
||||
} SMStack;
|
||||
|
||||
#define SM_INIT_STACK(stack) \
|
||||
((stack)->segment = NULL, \
|
||||
(stack)->top = NULL, \
|
||||
(stack)->min = NULL, \
|
||||
(stack)->max = NULL, \
|
||||
(stack)->overflowCount = 0) \
|
||||
|
||||
#define SM_STACK_POP0(stack) \
|
||||
(*(--(stack)->top)) \
|
||||
|
||||
#define SM_STACK_PUSH0(stack, obj) \
|
||||
((void)(*((stack)->top)++ = (obj))) \
|
||||
|
||||
#define SM_STACK_POP(stack) \
|
||||
(((stack)->top > (stack)->min) \
|
||||
? SM_STACK_POP0(stack) \
|
||||
: SM_STACK_UNDERFLOW(stack)) \
|
||||
|
||||
#define SM_STACK_PUSH(stack, obj) \
|
||||
(((stack)->top < (stack)->max) \
|
||||
? (SM_STACK_PUSH0(stack, obj), PR_TRUE) \
|
||||
: SM_STACK_OVERFLOW(stack, obj)) \
|
||||
|
||||
#define SM_STACK_UNDERFLOW(stack) sm_StackUnderflow(stack)
|
||||
|
||||
#define SM_STACK_OVERFLOW(stack, obj) sm_StackOverflow(stack, obj)
|
||||
|
||||
#define SM_STACK_IS_EMPTY(stack) \
|
||||
((stack)->segment == NULL \
|
||||
|| ((stack)->segment->header.prev == NULL \
|
||||
&& (stack)->top == (stack)->min)) \
|
||||
|
||||
extern SMObject*
|
||||
sm_StackUnderflow(SMStack* stack);
|
||||
|
||||
extern PRBool
|
||||
sm_StackOverflow(SMStack* stack, SMObject* obj);
|
||||
|
||||
extern PRUword
|
||||
sm_StackSize(SMStack* stack);
|
||||
|
||||
extern void
|
||||
sm_DestroyStack(SMStack* stack);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMSTACK__ */
|
||||
/******************************************************************************/
|
111
ef/gc/include/smtrav.h
Normal file
111
ef/gc/include/smtrav.h
Normal file
@ -0,0 +1,111 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __SMTRAV__
|
||||
#define __SMTRAV__
|
||||
|
||||
#include "smpriv.h"
|
||||
#include "smobj.h"
|
||||
#include "smgen.h"
|
||||
|
||||
SM_BEGIN_EXTERN_C
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
typedef PRWord
|
||||
(*SMTraverseFieldProc)(SMObject* field, PRUword fieldOffset, void* closure);
|
||||
|
||||
SM_EXTERN(PRWord)
|
||||
SM_TraverseObjectFields(SMObject* obj,
|
||||
SMTraverseFieldProc traverseProc, void* traverseClosure);
|
||||
|
||||
typedef PRWord
|
||||
(*SMTraverseObjectProc)(void* obj, SMSmallObjCount index,
|
||||
PRUword objSize, void* closure);
|
||||
|
||||
SM_EXTERN(PRWord)
|
||||
SM_TraversePageObjects(SMPage* page,
|
||||
SMTraverseObjectProc traverseProc, void* traverseClosure);
|
||||
|
||||
typedef PRWord
|
||||
(*SMTraversePageProc)(SMPage* page, void* closure);
|
||||
|
||||
SM_EXTERN(PRWord)
|
||||
SM_TraverseGenPages(SMGenNum genNum,
|
||||
SMTraversePageProc traverseProc, void* traverseClosure);
|
||||
|
||||
SM_EXTERN(PRWord)
|
||||
SM_TraverseGenObjects(SMGenNum genNum,
|
||||
SMTraverseObjectProc traverseProc, void* traverseClosure);
|
||||
|
||||
SM_EXTERN(PRWord)
|
||||
SM_TraverseAllPages(SMTraversePageProc traverseProc, void* traverseClosure);
|
||||
|
||||
SM_EXTERN(PRWord)
|
||||
SM_TraverseAllObjects(SMTraverseObjectProc traverseProc, void* traverseClosure);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef SM_DUMP /* define this if you want dump code in the final product */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum SMDumpFlag {
|
||||
SMDumpFlag_None = 0,
|
||||
SMDumpFlag_Detailed = 1, /* adds hex dump of objects */
|
||||
SMDumpFlag_GCState = 2, /* adds dump of unmarked and forwarded nodes */
|
||||
SMDumpFlag_Abbreviate = 4 /* only prints the beginning of large objects */
|
||||
} SMDumpFlag;
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DumpMallocHeap(FILE* out, PRUword flags);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DumpGCHeap(FILE* out, PRUword flags);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DumpPage(FILE* out, void* addr, PRUword flags);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DumpHeap(FILE* out, PRUword flags);
|
||||
|
||||
SM_EXTERN(void)
|
||||
SM_DumpPageMap(FILE* out, PRUword flags);
|
||||
|
||||
#endif /* SM_DUMP */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_END_EXTERN_C
|
||||
|
||||
#endif /* __SMTRAV__ */
|
||||
/******************************************************************************/
|
BIN
ef/gc/macbuild/SM.prj2
Normal file
BIN
ef/gc/macbuild/SM.prj2
Normal file
Binary file not shown.
4
ef/gc/macbuild/SMDebug.Prefix
Normal file
4
ef/gc/macbuild/SMDebug.Prefix
Normal file
@ -0,0 +1,4 @@
|
||||
#define DEBUG 1
|
||||
#undef NDEBUG
|
||||
|
||||
#include "NSPRConfig.h"
|
4
ef/gc/macbuild/SMRelease.Prefix
Normal file
4
ef/gc/macbuild/SMRelease.Prefix
Normal file
@ -0,0 +1,4 @@
|
||||
#undef DEBUG
|
||||
#define NDEBUG 1
|
||||
|
||||
#include "NSPRConfig.h"
|
BIN
ef/gc/macbuild/buckets.prj2
Normal file
BIN
ef/gc/macbuild/buckets.prj2
Normal file
Binary file not shown.
BIN
ef/gc/macbuild/divtest.prj2
Normal file
BIN
ef/gc/macbuild/divtest.prj2
Normal file
Binary file not shown.
BIN
ef/gc/macbuild/hashtest.prj2
Normal file
BIN
ef/gc/macbuild/hashtest.prj2
Normal file
Binary file not shown.
BIN
ef/gc/macbuild/maltest.prj2
Normal file
BIN
ef/gc/macbuild/maltest.prj2
Normal file
Binary file not shown.
BIN
ef/gc/macbuild/smtest.prj2
Normal file
BIN
ef/gc/macbuild/smtest.prj2
Normal file
Binary file not shown.
35
ef/gc/makefile.win
Normal file
35
ef/gc/makefile.win
Normal file
@ -0,0 +1,35 @@
|
||||
DEPTH=..
|
||||
|
||||
DIRS=include src test
|
||||
|
||||
include <$(DEPTH)\config\rules.mak>
|
||||
|
||||
mkdirs:
|
||||
-mkdir $(PUBLIC)\nspr20
|
||||
-mkdir $(DIST)
|
||||
-mkdir $(DIST)\lib
|
||||
-mkdir $(DIST)\bin
|
||||
|
||||
import_nspr20_incl:
|
||||
cp -r $(DEPTH)\nspr20\pr\include\* $(PUBLIC)\nspr20
|
||||
cp $(XPDIST)\WINNT4.0_DBG.OBJ\include\prcpucfg.h $(PUBLIC)\nspr20
|
||||
cp -r $(DEPTH)\nspr20\lib\ds\*.h $(PUBLIC)\nspr20
|
||||
cp -r $(DEPTH)\nspr20\lib\msgc\include\*.h $(PUBLIC)\nspr20
|
||||
|
||||
import_nspr20_libs: $(XPDIST)\WINNT4.0_DBG.OBJ\lib\*21.lib
|
||||
cp $** $(DIST)\lib
|
||||
|
||||
import_nspr20_dlls: $(XPDIST)\WINNT4.0_DBG.OBJ\lib\*21.dll
|
||||
cp $** $(DIST)\bin
|
||||
|
||||
import_nspr20: mkdirs import_nspr20_incl import_nspr20_libs import_nspr20_dlls
|
||||
|
||||
|
||||
|
||||
import_nspr20_libs_opt: $(XPDIST)\WINNT4.0_OPT.OBJ\lib\*21.lib
|
||||
cp $** $(DIST)\lib
|
||||
|
||||
import_nspr20_dlls_opt: $(XPDIST)\WINNT4.0_OPT.OBJ\lib\*21.dll
|
||||
cp $** $(DIST)\bin
|
||||
|
||||
import_nspr20_opt: mkdirs import_nspr20_incl import_nspr20_libs_opt import_nspr20_dlls_opt
|
4
ef/gc/src/Makefile
Normal file
4
ef/gc/src/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
DEPTH = ../..
|
||||
|
||||
include manifest.mn
|
||||
include $(DEPTH)/config/rules.mk
|
70
ef/gc/src/makefile.win
Normal file
70
ef/gc/src/makefile.win
Normal file
@ -0,0 +1,70 @@
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Makefile to build the machine independent java runtime library
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Specify the depth of the current directory relative to the
|
||||
#// root of NS
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
DEPTH= ..\..
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Define any Public Make Variables here: (ie. PDFFILE, MAPFILE, ...)
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
|
||||
DLLNAME = sm$(MOZ_BITS)$(VERSION_NUMBER)
|
||||
PDBFILE = $(DLLNAME).pdb
|
||||
MAPFILE = $(DLLNAME).map
|
||||
DLL =.\$(OBJDIR)\$(DLLNAME).dll
|
||||
MAKE_OBJ_TYPE = DLL
|
||||
|
||||
include <$(DEPTH)\config\config.mak>
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Define the files necessary to build the target (ie. OBJS)
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Define any Public Targets here (ie. PROGRAM, LIBRARY, DLL, ...)
|
||||
#// (these must be defined before the common makefiles are included)
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Define any local options for the make tools
|
||||
#// (ie. LCFLAGS, LLFLAGS, LLIBS, LINCS)
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
|
||||
#LCFLAGS = -DSM_NO_TABLE_DIVISION -DSM_DUMP -DSM_VERIFY=2
|
||||
LCFLAGS = -DSM_NO_TABLE_DIVISION -DSM_DUMP
|
||||
!ifndef MOZ_PROF # assume we use a write barrier when profiling
|
||||
LCFLAGS = $(LCFLAGS) -DSM_NO_WRITE_BARRIER -DSM_CHECK_PTRS -DSM_DEBUG_HEADER
|
||||
!endif
|
||||
|
||||
LLIBS= $(LLIBS) \
|
||||
$(DEPTH)\dist\WIN32_D.OBJ\lib\libnspr21.lib \
|
||||
$(DEPTH)\dist\WIN32_D.OBJ\lib\libplds21.lib \
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
#//
|
||||
#// Include the common makefile rules
|
||||
#//
|
||||
#//------------------------------------------------------------------------
|
||||
|
||||
include <$(DEPTH)\config\rules.mak>
|
||||
|
||||
install:: $(DLL)
|
||||
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin
|
||||
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib
|
22
ef/gc/src/manifest.mn
Normal file
22
ef/gc/src/manifest.mn
Normal file
@ -0,0 +1,22 @@
|
||||
MODULE = sm
|
||||
|
||||
LIBRARY_NAME = sm
|
||||
|
||||
DEPTH = ../..
|
||||
|
||||
CSRCS = \
|
||||
smassert.c \
|
||||
smobj.c \
|
||||
smgen.c \
|
||||
smheap.c \
|
||||
smpage.c \
|
||||
smpool.c \
|
||||
smstack.c \
|
||||
smgc.c \
|
||||
smalloc.c \
|
||||
smtrav.c \
|
||||
smhash.c \
|
||||
$(NULL)
|
||||
|
||||
REQUIRES = sm nspr20
|
||||
|
300
ef/gc/src/smalloc.c
Normal file
300
ef/gc/src/smalloc.c
Normal file
@ -0,0 +1,300 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smheap.h"
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SMObjStruct*
|
||||
sm_AddGCPage(SMGenNum genNum, SMBucket bucket)
|
||||
{
|
||||
SMObjStruct* obj = NULL;
|
||||
SMPage* page = SM_NEW_PAGE(&sm_PageMgr);
|
||||
if (page) {
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMSmallObjCount objTableSize = sm_ObjectsPerPage[bucket];
|
||||
SMObjDesc* objTable;
|
||||
|
||||
if (bucket >= SM_FIRST_MIDSIZE_BUCKET) {
|
||||
/* add an extra object descriptor for any extra space at the
|
||||
end of the page due to a non-integral number of objects */
|
||||
objTableSize++;
|
||||
}
|
||||
SM_ASSERT((objTableSize * objSize) >= SM_PAGE_SIZE);
|
||||
objTable = (SMObjDesc*)SM_Malloc(sizeof(SMObjDesc) * objTableSize);
|
||||
if (objTable == NULL) {
|
||||
SM_DESTROY_PAGE(&sm_PageMgr, page);
|
||||
return NULL;
|
||||
}
|
||||
obj = (SMObjStruct*)sm_PoolInitPage(&sm_Heap.gen[genNum].pool,
|
||||
genNum, bucket, page, objTable, 0);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Allocator
|
||||
******************************************************************************/
|
||||
|
||||
SMObjStruct*
|
||||
sm_Alloc(PRUword instanceSize, SMBucket bucket)
|
||||
{
|
||||
SMObjStruct* obj;
|
||||
SMGen* gen = &sm_Heap.gen[SMGenNum_Freshman];
|
||||
SMGenNum collectGenNum = SMGenNum_Free;
|
||||
PRUword objSize;
|
||||
|
||||
/* First force a collect if we've crossed our allocation threshold.
|
||||
Collect the oldest generations that has crossed its threshold,
|
||||
and all younger generations. */
|
||||
if (gen->pool.allocAmount >= gen->allocThreshold) {
|
||||
SMGenNum i;
|
||||
for (collectGenNum = SMGenNum_Freshman; collectGenNum <= SMGenNum_Static; collectGenNum++) {
|
||||
gen = &sm_Heap.gen[collectGenNum];
|
||||
if (gen->pool.allocAmount < gen->allocThreshold)
|
||||
break;
|
||||
}
|
||||
--collectGenNum;
|
||||
sm_Collect0(collectGenNum, SM_IS_COPY_CYCLE(collectGenNum));
|
||||
/* reset allocation cnnnounts */
|
||||
for (i = SMGenNum_Freshman; i < collectGenNum; i++) {
|
||||
sm_Heap.gen[i].pool.allocAmount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
gen = &sm_Heap.gen[SMGenNum_Freshman];
|
||||
|
||||
if (instanceSize > SM_MAX_SMALLOBJECT_SIZE) {
|
||||
do {
|
||||
SM_GEN_ALLOC_LARGE_OBJ(obj, gen, SMGenNum_Freshman, instanceSize);
|
||||
} while (obj == NULL &&
|
||||
(sm_CompactHook ? sm_CompactHook(instanceSize) : PR_FALSE));
|
||||
if (obj == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Don't add to the gen->pool.allocAmount for large objects --
|
||||
they don't contribute to triggering a gc. */
|
||||
|
||||
objSize = SM_PAGE_WIDTH(SM_PAGE_COUNT(instanceSize));
|
||||
/* XXX Should large objects factor into the allocAmount that
|
||||
we use to cause a collection? */
|
||||
}
|
||||
else {
|
||||
do {
|
||||
SM_GEN_ALLOC_OBJ(obj, gen, bucket);
|
||||
if (obj == NULL) {
|
||||
/* no objects on the free list -- try allocating a new page */
|
||||
obj = sm_AddGCPage(SMGenNum_Freshman, bucket);
|
||||
}
|
||||
} while (obj == NULL &&
|
||||
(sm_CompactHook ? sm_CompactHook(sm_ObjectSize[bucket]) : PR_FALSE));
|
||||
if (obj == NULL)
|
||||
return NULL;
|
||||
objSize = sm_ObjectSize[bucket];
|
||||
gen->pool.allocAmount += objSize;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
{
|
||||
SMPageDesc* pd = SM_OBJECT_PAGEDESC(obj);
|
||||
SMObjDesc* od = SM_OBJECT_HEADER_FROM_PAGEDESC(obj, pd);
|
||||
SM_ASSERT(SM_OBJDESC_IS_ALLOCATED(od));
|
||||
}
|
||||
#endif
|
||||
SM_POOL_SET_ALLOC_STATS(&gen->pool, objSize, instanceSize);
|
||||
DBG_MEMSET(obj, SM_UNUSED_PATTERN, instanceSize);
|
||||
SM_SET_DEBUG_HEADER(obj, bucket, SMGenNum_Freshman);
|
||||
return obj;
|
||||
}
|
||||
|
||||
#define SM_INIT_OBJECT(obj, clazz) \
|
||||
{ \
|
||||
SMObject* _obj = (obj); \
|
||||
SMClass* _clazz = (SMClass*)(clazz); \
|
||||
/* Initialize reference fields */ \
|
||||
SMFieldDesc* _fieldDesc = SM_CLASS_GET_INST_FIELD_DESCS(_clazz); \
|
||||
PRUint16 i, _fdCount = SM_CLASS_GET_INST_FIELDS_COUNT(_clazz); \
|
||||
for (i = 0; i < _fdCount; i++) { \
|
||||
PRWord _offset = _fieldDesc[i].offset; \
|
||||
PRUword _count = _fieldDesc[i].count; \
|
||||
SMObject** _fieldRefs = &SM_OBJECT_FIELDS(_obj)[_offset]; \
|
||||
SM_UNROLLED_WHILE(_count, { \
|
||||
*_fieldRefs++ = NULL; \
|
||||
}); \
|
||||
} \
|
||||
SM_OBJECT_CLASS(_obj) = _clazz; \
|
||||
if (SM_CLASS_IS_FINALIZABLE(_clazz)) { \
|
||||
SMObjStruct* _dobj = SM_DECRYPT_OBJECT(_obj); \
|
||||
SMPageDesc* _pd = SM_OBJECT_PAGEDESC(_dobj); \
|
||||
SMObjDesc* _od = SM_OBJECT_HEADER_FROM_PAGEDESC(_dobj, _pd); \
|
||||
SM_OBJDESC_SET_FINALIZABLE(_od); \
|
||||
} \
|
||||
} \
|
||||
|
||||
SM_IMPLEMENT(SMObject*)
|
||||
SM_AllocObject(SMObjectClass* clazz)
|
||||
{
|
||||
SMObjStruct* obj;
|
||||
PRUword instanceSize = SM_CLASS_INSTANCE_SIZE(clazz);
|
||||
SMBucket bucket = SM_CLASS_BUCKET(clazz);
|
||||
PRMonitor* mon = sm_Heap.gen[SMGenNum_Freshman].pool.sweepList[bucket].monitor;
|
||||
|
||||
SM_ASSERT(SM_IS_OBJECT_CLASS(clazz));
|
||||
|
||||
PR_EnterMonitor(mon);
|
||||
obj = sm_Alloc(instanceSize, bucket);
|
||||
if (obj) {
|
||||
SMObject* eobj = SM_ENCRYPT_OBJECT(obj);
|
||||
SM_INIT_OBJECT(eobj, clazz);
|
||||
}
|
||||
SM_VERIFY_HEAP();
|
||||
PR_ExitMonitor(mon);
|
||||
return SM_ENCRYPT_OBJECT(obj);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_IMPLEMENT(SMArray*)
|
||||
SM_AllocArray(SMArrayClass* clazz, PRUword size)
|
||||
{
|
||||
SMArrayStruct* arr;
|
||||
PRUword instanceSize;
|
||||
SMBucket bucket;
|
||||
PRMonitor* mon;
|
||||
int eltSize = SM_ELEMENT_SIZE(clazz);
|
||||
|
||||
SM_ASSERT(SM_IS_ARRAY_CLASS(clazz));
|
||||
|
||||
instanceSize = sizeof(SMArrayStruct) + (eltSize * size);
|
||||
#ifdef DEBUG
|
||||
/* In debug mode, we'll store a pointer back to the beginning
|
||||
of the array in the last element of the array. This lets us
|
||||
easily see where the array ends in memory. */
|
||||
if (eltSize == sizeof(void*)) {
|
||||
instanceSize += sizeof(void*);
|
||||
}
|
||||
#endif
|
||||
SM_GET_ALLOC_BUCKET(bucket, instanceSize);
|
||||
|
||||
mon = sm_Heap.gen[SMGenNum_Freshman].pool.sweepList[bucket].monitor;
|
||||
PR_EnterMonitor(mon);
|
||||
arr = (SMArrayStruct*)sm_Alloc(instanceSize, bucket);
|
||||
if (arr) {
|
||||
SMArray* earr = SM_ENCRYPT_ARRAY(arr);
|
||||
SM_ARRAY_CLASS(earr) = (SMClass*)clazz;
|
||||
SM_ARRAY_SIZE(earr) = size;
|
||||
if (SM_IS_OBJECT_ARRAY_CLASS(clazz)) {
|
||||
SMObject** elements = SM_ARRAY_ELEMENTS(earr);
|
||||
SM_UNROLLED_WHILE(size, {
|
||||
SM_ASSERT(!SM_CHECK_DEBUG_HEADER(*elements));
|
||||
*elements++ = NULL;
|
||||
});
|
||||
#ifdef DEBUG
|
||||
if (eltSize == sizeof(void*)) {
|
||||
*elements = (SMObject*)arr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
SM_VERIFY_HEAP();
|
||||
PR_ExitMonitor(mon);
|
||||
return SM_ENCRYPT_ARRAY(arr);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_IMPLEMENT(SMObject*)
|
||||
SM_AllocObjectExtra(SMObjectClass* clazz, PRUword extraBytes)
|
||||
{
|
||||
SMObjStruct* obj;
|
||||
PRUword instanceSize;
|
||||
SMBucket bucket;
|
||||
PRMonitor* mon;
|
||||
|
||||
SM_ASSERT(SM_IS_OBJECT_CLASS(clazz));
|
||||
|
||||
instanceSize = SM_CLASS_INSTANCE_SIZE(clazz) + extraBytes;
|
||||
SM_GET_ALLOC_BUCKET(bucket, instanceSize);
|
||||
|
||||
mon = sm_Heap.gen[SMGenNum_Freshman].pool.sweepList[bucket].monitor;
|
||||
PR_EnterMonitor(mon);
|
||||
obj = sm_Alloc(instanceSize, bucket);
|
||||
if (obj) {
|
||||
SMObject* eobj = SM_ENCRYPT_OBJECT(obj);
|
||||
SM_INIT_OBJECT(eobj, clazz);
|
||||
memset(SM_OBJECT_EXTRA(eobj), 0, extraBytes);
|
||||
}
|
||||
SM_VERIFY_HEAP();
|
||||
PR_ExitMonitor(mon);
|
||||
return SM_ENCRYPT_OBJECT(obj);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_IMPLEMENT(PRUword)
|
||||
SM_ObjectSize(SMObject* obj)
|
||||
{
|
||||
SMClass* clazz = SM_OBJECT_CLASS(obj);
|
||||
PRUword size;
|
||||
SM_ASSERT(clazz);
|
||||
switch (SM_CLASS_KIND(clazz)) {
|
||||
case SMClassKind_ObjectClass:
|
||||
size = SM_CLASS_INSTANCE_SIZE(clazz);
|
||||
break;
|
||||
case SMClassKind_ArrayClass:
|
||||
size = sizeof(SMArrayStruct) +
|
||||
SM_ARRAY_SIZE((SMArray*)obj) * SM_ELEMENT_SIZE(clazz);
|
||||
break;
|
||||
default:
|
||||
size = 0;
|
||||
}
|
||||
SM_ASSERT(size <= sm_ObjectSize[SM_PAGEDESC_BUCKET(SM_OBJECT_PAGEDESC(SM_DECRYPT_OBJECT(obj)))]);
|
||||
return size;
|
||||
}
|
||||
|
||||
SM_EXTERN(SMObject*)
|
||||
SM_Clone(SMObject* obj)
|
||||
{
|
||||
SMObjStruct* dobj = SM_DECRYPT_OBJECT(obj);
|
||||
SMPageDesc* pd = SM_OBJECT_PAGEDESC(dobj);
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
PRUword instanceSize;
|
||||
SMObjStruct* result;
|
||||
|
||||
SM_OBJECT_GROSS_SIZE(&instanceSize, dobj);
|
||||
result = sm_Alloc(instanceSize, bucket);
|
||||
if (result) {
|
||||
memcpy(result, obj, instanceSize);
|
||||
}
|
||||
return SM_ENCRYPT_OBJECT(result);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
101
ef/gc/src/smassert.c
Normal file
101
ef/gc/src/smassert.c
Normal file
@ -0,0 +1,101 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include "smtrav.h"
|
||||
#include "smheap.h"
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_Assert(const char* ex, const char* file, int line)
|
||||
{
|
||||
char buf[400];
|
||||
|
||||
PR_snprintf(buf, sizeof(buf),
|
||||
"Assertion failure: %s, in file \"%s\" line %d\n",
|
||||
ex, file, line);
|
||||
|
||||
#ifndef XP_MAC /* takes too long */
|
||||
fprintf(sm_DebugOutput, buf);
|
||||
SM_DumpStats(sm_DebugOutput, PR_TRUE);
|
||||
SM_DumpHeap(sm_DebugOutput, SMDumpFlag_Detailed | SMDumpFlag_GCState);
|
||||
SM_DumpPageMap(sm_DebugOutput, SMDumpFlag_Detailed | SMDumpFlag_GCState);
|
||||
#endif
|
||||
fflush(sm_DebugOutput);
|
||||
|
||||
#ifdef XP_UNIX
|
||||
fprintf(stderr, buf);
|
||||
#endif
|
||||
|
||||
#ifdef XP_MAC
|
||||
{
|
||||
Str255 debugStr;
|
||||
uint32 length;
|
||||
|
||||
length = strlen(buf);
|
||||
if (length > 255)
|
||||
length = 255;
|
||||
debugStr[0] = length;
|
||||
memcpy(debugStr + 1, buf, length);
|
||||
DebugStr(debugStr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(XP_PC) && defined(DEBUG)
|
||||
{
|
||||
char* msg = PR_smprintf("%s\nPress Retry to debug the application.", buf);
|
||||
int status = MessageBox((HWND)NULL, msg, "NSPR Assertion",
|
||||
MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION);
|
||||
PR_smprintf_free(msg);
|
||||
switch (status) {
|
||||
case IDABORT:
|
||||
#ifdef _WIN32
|
||||
PR_Abort();
|
||||
#endif
|
||||
break;
|
||||
case IDRETRY:
|
||||
DebugBreak();
|
||||
break;
|
||||
case IDIGNORE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* XP_PC && DEBUG */
|
||||
|
||||
#if !defined(XP_PC)
|
||||
PR_Abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/******************************************************************************/
|
1106
ef/gc/src/smgc.c
Normal file
1106
ef/gc/src/smgc.c
Normal file
File diff suppressed because it is too large
Load Diff
124
ef/gc/src/smgen.c
Normal file
124
ef/gc/src/smgen.c
Normal file
@ -0,0 +1,124 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smgen.h"
|
||||
#include "smheap.h"
|
||||
#include <string.h>
|
||||
|
||||
PRStatus
|
||||
sm_InitGen(SMGen* gen, PRUword allocThreshold)
|
||||
{
|
||||
gen->allocThreshold = allocThreshold;
|
||||
gen->collectCount = 0;
|
||||
return sm_InitPool(&gen->pool);
|
||||
}
|
||||
|
||||
void
|
||||
sm_FiniGen(SMGen* gen)
|
||||
{
|
||||
sm_FiniPool(&gen->pool);
|
||||
gen->allocThreshold = 0;
|
||||
gen->collectCount = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SMObjStruct*
|
||||
sm_GenPromoteObj(SMGen* gen, SMBucket bucket)
|
||||
{
|
||||
SMSweepList* sweepList = &gen->pool.sweepList[bucket];
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMPageDesc* pd;
|
||||
|
||||
/*SM_ASSERT(SM_IN_GC());*/
|
||||
|
||||
/* pages on the sweep list should never be large */
|
||||
SM_ASSERT(bucket != SM_LARGE_OBJECT_BUCKET);
|
||||
|
||||
/* lazy sweep -- start from where we left off, and only go until
|
||||
we find what we need */
|
||||
while ((pd = sweepList->sweepPage) != NULL) {
|
||||
SM_ASSERT(SM_PAGEDESC_BUCKET(pd) == bucket);
|
||||
/* The pd->allocCount may be zero here because we may be collecting
|
||||
the generation we're currently allocating in.
|
||||
SM_ASSERT(pd->allocCount != 0); */
|
||||
while (sweepList->sweepIndex < objsPerPage) {
|
||||
SMSmallObjCount i = sweepList->sweepIndex++;
|
||||
SMObjDesc* od = &pd->objTable[i];
|
||||
if (SM_OBJDESC_WAS_FREE(od)) { /* only line different from sm_PoolAllocObj */
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMObjStruct* obj = (SMObjStruct*)((char*)SM_PAGEDESC_PAGE(pd) + i * objSize);
|
||||
SM_OBJDESC_SET_ALLOCATED(od);
|
||||
if (++pd->allocCount == objsPerPage) {
|
||||
/* if this allocation caused this page to become
|
||||
full, remove the page from the sweep list */
|
||||
sweepList->sweepPage = pd->next;
|
||||
pd->next = NULL;
|
||||
sweepList->sweepIndex = 0;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
sweepList->sweepPage = pd->next;
|
||||
pd->next = NULL;
|
||||
sweepList->sweepIndex = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
PRUword
|
||||
sm_GenSpaceAvailable(SMGenNum genNum)
|
||||
{
|
||||
SMPageCount i;
|
||||
PRUword space = 0;
|
||||
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
|
||||
SMGenNum pdGen = SM_PAGEDESC_GEN(pd);
|
||||
if (pdGen == genNum) {
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
if (bucket != SM_LARGE_OBJECT_BUCKET) {
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
space += objSize * (objsPerPage - pd->allocCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/******************************************************************************/
|
188
ef/gc/src/smhash.c
Normal file
188
ef/gc/src/smhash.c
Normal file
@ -0,0 +1,188 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smhash.h"
|
||||
|
||||
#define SM_MASK(n) ((1U << (n)) - 1)
|
||||
|
||||
#define SM_HASHTABLE_SMALLEST_POWER 6
|
||||
#define SM_HASHTABLE_LARGEST_POWER 32
|
||||
|
||||
PRUword sm_HashMasks[SM_HASHTABLE_SIZES] = {
|
||||
SM_MASK(6),
|
||||
SM_MASK(7),
|
||||
SM_MASK(8),
|
||||
SM_MASK(9),
|
||||
SM_MASK(10),
|
||||
SM_MASK(11),
|
||||
SM_MASK(12),
|
||||
SM_MASK(13),
|
||||
SM_MASK(14),
|
||||
SM_MASK(15),
|
||||
SM_MASK(16),
|
||||
SM_MASK(17),
|
||||
SM_MASK(18),
|
||||
SM_MASK(19),
|
||||
SM_MASK(20),
|
||||
SM_MASK(21),
|
||||
SM_MASK(22),
|
||||
SM_MASK(23),
|
||||
SM_MASK(24),
|
||||
SM_MASK(25),
|
||||
SM_MASK(26),
|
||||
SM_MASK(27),
|
||||
SM_MASK(28),
|
||||
SM_MASK(29),
|
||||
SM_MASK(30),
|
||||
SM_MASK(31),
|
||||
0xffffffff
|
||||
};
|
||||
|
||||
PRUword sm_HashPrimes[SM_HASHTABLE_SIZES] = {
|
||||
61,
|
||||
127,
|
||||
251,
|
||||
509,
|
||||
1021,
|
||||
2039,
|
||||
4093,
|
||||
8191,
|
||||
16381,
|
||||
32749,
|
||||
65521,
|
||||
131071,
|
||||
262139,
|
||||
524287,
|
||||
1048573,
|
||||
2097143,
|
||||
4194301,
|
||||
8388593,
|
||||
16777213,
|
||||
33554393,
|
||||
67108859,
|
||||
134217689,
|
||||
268435399,
|
||||
536870909,
|
||||
1073741789,
|
||||
2147483647,
|
||||
4294967291
|
||||
};
|
||||
|
||||
static void
|
||||
sm_TestHashTables(void)
|
||||
{
|
||||
PRUword i;
|
||||
for (i = SM_HASHTABLE_SMALLEST_POWER; i <= SM_HASHTABLE_LARGEST_POWER; i++) {
|
||||
PRUword size = (1 << i);
|
||||
SM_ASSERT(sm_HashMasks[i] + 1 == size);
|
||||
SM_ASSERT(sm_HashPrimes[i] < size);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define SM_GOLDEN_RATIO 616161
|
||||
#define SM_HASH1(key, size) (((key) * SM_GOLDEN_RATIO) & size)
|
||||
#define SM_HASH2(key, size) (((key) * 97) + 1)
|
||||
|
||||
SMArrayClass sm_HashTableClass;
|
||||
|
||||
SM_IMPLEMENT(SMHashTable*)
|
||||
SM_NewHashTable(PRUword initialSize, PRUword maxSize)
|
||||
{
|
||||
SMArray* obj;
|
||||
static PRBool hashTableClassInitialized = PR_FALSE;
|
||||
|
||||
if (!hashTableClassInitialized) {
|
||||
SMClass* elementClass = NULL; /* XXX */
|
||||
SM_InitArrayClass(&sm_HashTableClass, NULL, elementClass);
|
||||
hashTableClassInitialized = PR_TRUE;
|
||||
}
|
||||
|
||||
obj = SM_AllocArray(&sm_HashTableClass, initialSize);
|
||||
return (SMHashTable*)obj;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(SMObject*)
|
||||
SM_HashTableLookup(SMHashTable* self, PRUword key)
|
||||
{
|
||||
PRUword htSize = self->array.size;
|
||||
PRUword hash1 = SM_HASH1(key, htSize);
|
||||
PRUword hash2 = SM_HASH2(key, htSize);
|
||||
PRUword i = hash1;
|
||||
SMHashEntry* elements = self->entry;
|
||||
|
||||
SM_ASSERT(key != 0);
|
||||
while (elements[i].key != 0) {
|
||||
if (elements[i].key == key)
|
||||
return elements[i].value;
|
||||
else {
|
||||
i = (i + hash2) % htSize;
|
||||
SM_ASSERT(i != hash1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_HashTableInsert(SMHashTable* self, PRUword key, SMObject* value)
|
||||
{
|
||||
PRUword htSize = self->array.size;
|
||||
PRUword hash1 = SM_HASH1(key, htSize);
|
||||
PRUword hash2 = SM_HASH2(key, htSize);
|
||||
PRUword i = hash1;
|
||||
SMHashEntry* elements = self->entry;
|
||||
|
||||
SM_ASSERT(key != 0);
|
||||
while (elements[i].key != 0) {
|
||||
i = (i + hash2) % htSize;
|
||||
SM_ASSERT(i != hash1);
|
||||
}
|
||||
elements[i].key = key;
|
||||
elements[i].value = value;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(SMObject*)
|
||||
SM_HashTableRemove(SMHashTable* self, PRUword key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(SMObject*)
|
||||
SM_HashTableDelete(SMHashTable* self, PRUword key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
523
ef/gc/src/smheap.c
Normal file
523
ef/gc/src/smheap.c
Normal file
@ -0,0 +1,523 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smheap.h"
|
||||
#include <string.h>
|
||||
|
||||
PRUint8* SM_CardTable;
|
||||
SMHeap sm_Heap; /* _the_ global heap */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static PRBool gcInitialized = PR_FALSE;
|
||||
|
||||
SM_IMPLEMENT(PRStatus)
|
||||
SM_InitGC(PRUword initialHeapSize, PRUword maxHeapSize,
|
||||
SMGCHookProc beforeGCHook, void* beforeGCClosure,
|
||||
SMGCHookProc afterGCHook, void* afterGCClosure,
|
||||
SMMarkRootsProc markRootsProc, void* markRootsClosure)
|
||||
{
|
||||
PRStatus status;
|
||||
PRUword i;
|
||||
#ifndef SM_NO_WRITE_BARRIER
|
||||
PRUword cardTableSize;
|
||||
#endif
|
||||
|
||||
if (gcInitialized)
|
||||
return PR_SUCCESS;
|
||||
gcInitialized = PR_TRUE;
|
||||
|
||||
/* Make sure the page descriptor generation field is big enough. */
|
||||
SM_ASSERT(SMGenNum_Free <= (1 << SM_PAGEDESC_GEN_BITS));
|
||||
|
||||
memset(&sm_Heap, 0, sizeof(SMHeap)); /* in case we fail */
|
||||
|
||||
#ifndef SM_NO_WRITE_BARRIER
|
||||
cardTableSize = SM_PAGE_COUNT(initialHeapSize) * sizeof(SMCardDesc);
|
||||
initialHeapSize += cardTableSize;
|
||||
#endif
|
||||
|
||||
status = SM_InitMalloc(initialHeapSize, maxHeapSize);
|
||||
if (status != PR_SUCCESS)
|
||||
return status;
|
||||
|
||||
/* Initialize generations */
|
||||
for (i = SMGenNum_Freshman; i <= SMGenNum_Static; i++) {
|
||||
status = sm_InitGen(&sm_Heap.gen[i], SM_DEFAULT_ALLOC_THRESHOLD);
|
||||
if (status != PR_SUCCESS) goto fail;
|
||||
}
|
||||
|
||||
/* Initialize tables */
|
||||
#ifndef SM_NO_WRITE_BARRIER
|
||||
sm_Heap.cardTablePageCount = SM_PAGE_COUNT(cardTableSize);
|
||||
sm_Heap.cardTableMem = (SMCardDesc*)sm_NewCluster(&sm_PageMgr,
|
||||
sm_Heap.cardTablePageCount);
|
||||
if (sm_Heap.cardTableMem == NULL) goto fail;
|
||||
SM_CardTable = sm_Heap.cardTableMem - (SM_PAGE_NUMBER(sm_PageMgr.heapBase) * sizeof(SMCardDesc));
|
||||
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
|
||||
SMCardDesc* cd = &sm_Heap.cardTableMem[i];
|
||||
SM_CARDDESC_INIT(cd);
|
||||
}
|
||||
#endif
|
||||
|
||||
SM_INIT_STACK(&sm_Heap.markStack);
|
||||
|
||||
sm_Heap.markRootsProc = markRootsProc;
|
||||
sm_Heap.markRootsClosure = markRootsClosure;
|
||||
sm_Heap.beforeGCHook = beforeGCHook;
|
||||
sm_Heap.beforeGCClosure = beforeGCClosure;
|
||||
sm_Heap.afterGCHook = afterGCHook;
|
||||
sm_Heap.afterGCClosure = afterGCClosure;
|
||||
sm_Heap.collectingGenNum = SMGenNum_Free;
|
||||
|
||||
status = sm_InitFinalizer();
|
||||
if (status != PR_SUCCESS)
|
||||
goto fail;
|
||||
|
||||
sm_CompactHook = sm_CollectAndFinalizeAll;
|
||||
|
||||
return PR_SUCCESS;
|
||||
|
||||
fail:
|
||||
SM_CleanupGC(PR_FALSE);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRStatus)
|
||||
SM_CleanupGC(PRBool finalizeOnExit)
|
||||
{
|
||||
int i;
|
||||
PRStatus status;
|
||||
|
||||
status = sm_FiniFinalizer(finalizeOnExit);
|
||||
/* do the following even if finalize-on-exit failed */
|
||||
|
||||
sm_CompactHook = NULL;
|
||||
|
||||
sm_DestroyStack(&sm_Heap.markStack);
|
||||
|
||||
for (i = SMGenNum_Freshman; i <= SMGenNum_Static; i++) {
|
||||
sm_FiniGen(&sm_Heap.gen[i]);
|
||||
}
|
||||
|
||||
#ifndef SM_NO_WRITE_BARRIER
|
||||
sm_DestroyCluster(&sm_PageMgr,
|
||||
(SMPage*)sm_Heap.cardTableMem,
|
||||
sm_Heap.cardTablePageCount);
|
||||
SM_CardTable = NULL;
|
||||
#endif
|
||||
|
||||
SM_CleanupMalloc();
|
||||
|
||||
gcInitialized = PR_FALSE;
|
||||
return status;
|
||||
}
|
||||
|
||||
#if !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG)
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DirtyPage(void* addr)
|
||||
{
|
||||
SMCardDesc* cd = &SM_CardTable[SM_PAGE_NUMBER(addr)];
|
||||
SMCardDesc* cardTableLimit = (SMCardDesc*)
|
||||
((char*)sm_Heap.cardTableMem + SM_PAGE_WIDTH(sm_Heap.cardTablePageCount));
|
||||
SM_ASSERT(sm_Heap.cardTableMem <= cd && cd < cardTableLimit);
|
||||
*cd = 0;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_CleanPage(void* addr)
|
||||
{
|
||||
SMCardDesc* cd = &SM_CardTable[SM_PAGE_NUMBER(addr)];
|
||||
SMCardDesc* cardTableLimit = (SMCardDesc*)
|
||||
((char*)sm_Heap.cardTableMem + SM_PAGE_WIDTH(sm_Heap.cardTablePageCount));
|
||||
SM_ASSERT(sm_Heap.cardTableMem <= cd && cd < cardTableLimit);
|
||||
*cd = SMGenNum_Free + 1;
|
||||
}
|
||||
|
||||
#endif /* !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG) */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef SM_STATS
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_Stats(SMStats* stats)
|
||||
{
|
||||
SMPageCount i;
|
||||
PRUword totalAmountRequested = 0, totalRequests = 0;
|
||||
|
||||
memset(stats, 0, sizeof(SMStats));
|
||||
|
||||
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
|
||||
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
|
||||
if (genNum != SMGenNum_Free) {
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
if (bucket == SM_LARGE_OBJECT_BUCKET) {
|
||||
SMSmallObjSize objSize = pd->allocCount * SM_PAGE_SIZE;
|
||||
PRUword overhead = pd->allocCount * sizeof(SMPageDesc);
|
||||
stats->perGen[genNum].amountUsed += objSize;
|
||||
stats->perGen[genNum].overhead += overhead;
|
||||
}
|
||||
else {
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
PRUword amountUsed = pd->allocCount * objSize;
|
||||
PRUword amountFree = (objsPerPage - pd->allocCount) * objSize;
|
||||
PRUword overhead = sizeof(SMPageDesc)
|
||||
+ objsPerPage /* object table */
|
||||
+ SM_PAGE_SIZE - (objsPerPage * objSize); /* unused space at end of page */
|
||||
stats->perGen[genNum].amountFree += amountFree;
|
||||
stats->perGen[genNum].amountUsed += amountUsed;
|
||||
stats->perGen[genNum].overhead += overhead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stats->systemOverhead =
|
||||
sizeof(SMHeap) +
|
||||
sm_PageMgr.heapPageCount * sizeof(SMPageDesc) +
|
||||
#ifndef SM_NO_WRITE_BARRIER
|
||||
sm_PageMgr.heapPageCount * sizeof(SMCardDesc) +
|
||||
#endif
|
||||
sizeof(SMSmallObjSize[SM_ALLOC_BUCKETS]) + /* sm_ObjectSize */
|
||||
sizeof(SMSmallObjCount[SM_ALLOC_BUCKETS]) + /* sm_ObjectsPerPage */
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
sizeof(SMSmallObjCount[SM_DIVTABLE_ENTRY_COUNT]) + /* sm_ObjectSizeDivTableMem */
|
||||
sizeof(SMSmallObjCount*[SM_ALLOC_BUCKETS]) + /* sm_ObjectSizeDivTable */
|
||||
#endif
|
||||
sizeof(SMBucket[SM_ALLOC_BUCKETS - 1]); /* sm_AntiBucket */
|
||||
|
||||
stats->systemOverhead += sm_StackSize(&sm_Heap.markStack);
|
||||
|
||||
for (i = SMGenNum_Freshman; i <= SMGenNum_Malloc; i++) {
|
||||
stats->total.amountFree += stats->perGen[i].amountFree;
|
||||
stats->total.amountUsed += stats->perGen[i].amountUsed;
|
||||
stats->total.overhead += stats->perGen[i].overhead;
|
||||
if (i < SMGenNum_Malloc)
|
||||
stats->perGen[i].collections = sm_Heap.gen[i].collectCount;
|
||||
stats->total.collections += stats->perGen[i].collections;
|
||||
|
||||
if (i < SMGenNum_Malloc) {
|
||||
stats->total.totalAllocated +=
|
||||
stats->perGen[i].totalAllocated = sm_Heap.gen[i].pool.totalAllocated;
|
||||
stats->total.totalRequested +=
|
||||
stats->perGen[i].totalRequested = sm_Heap.gen[i].pool.totalRequested;
|
||||
}
|
||||
}
|
||||
stats->total.overhead += stats->systemOverhead;
|
||||
}
|
||||
|
||||
static void
|
||||
sm_DumpStat(FILE* out, char* name, PRUword used, PRUword free, PRUword over,
|
||||
PRUword totalAllocated, PRUword totalRequested, PRUword collections)
|
||||
{
|
||||
PRUword total = used + free + over;
|
||||
fprintf(out, " %-8s %10u (%2u%%) %10u (%2u%%) %10u (%2u%%) ",
|
||||
name,
|
||||
used, (total ? (used * 100 / total) : 0),
|
||||
free, (total ? (free * 100 / total) : 0),
|
||||
over, (total ? (over * 100 / total) : 0),
|
||||
collections);
|
||||
if (totalAllocated) {
|
||||
PRUword frag = (totalAllocated
|
||||
? (totalAllocated - totalRequested) * 100 / totalAllocated
|
||||
: 0);
|
||||
fprintf(out, " %3u%%", frag);
|
||||
}
|
||||
else
|
||||
fprintf(out, " ");
|
||||
if (collections)
|
||||
fprintf(out, " %7u", collections);
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DumpStats(FILE* out, PRBool detailed)
|
||||
{
|
||||
int i;
|
||||
char* genName[SM_GEN_COUNT] =
|
||||
{ " Gen 0", " Gen 1", " Gen 2", " Gen 3", " Static" };
|
||||
PRUword gcUsed = 0, gcFree = 0, gcOver = 0;
|
||||
PRUword gcTotalAllocated = 0, gcTotalRequested = 0;
|
||||
PRUword used, free, over, frag;
|
||||
SMStats stats;
|
||||
SM_Stats(&stats);
|
||||
fprintf(out, "--SportModel-Statistics-----------------------------------------------------\n");
|
||||
fprintf(out, " SPACE USED FREE OVERHEAD FRAG COLLECT\n");
|
||||
for (i = SMGenNum_Freshman; i < SMGenNum_Malloc; i++) {
|
||||
used = stats.perGen[i].amountUsed;
|
||||
free = stats.perGen[i].amountFree;
|
||||
over = stats.perGen[i].overhead;
|
||||
if (detailed) {
|
||||
sm_DumpStat(out, genName[i], used, free, over,
|
||||
stats.perGen[i].totalAllocated, stats.perGen[i].totalRequested,
|
||||
stats.perGen[i].collections);
|
||||
}
|
||||
gcUsed += used;
|
||||
gcFree += free;
|
||||
gcOver += over;
|
||||
gcTotalAllocated += stats.perGen[i].totalAllocated;
|
||||
gcTotalRequested += stats.perGen[i].totalRequested;
|
||||
}
|
||||
frag = (gcTotalAllocated
|
||||
? (gcTotalAllocated - gcTotalRequested) * 100 / gcTotalAllocated
|
||||
: 0);
|
||||
sm_DumpStat(out, "GC Total", gcUsed, gcFree, gcOver,
|
||||
gcTotalAllocated, gcTotalRequested,
|
||||
stats.perGen[SMGenNum_Freshman].collections);
|
||||
used = stats.perGen[SMGenNum_Malloc].amountUsed;
|
||||
free = stats.perGen[SMGenNum_Malloc].amountFree;
|
||||
over = stats.perGen[SMGenNum_Malloc].overhead;
|
||||
sm_DumpStat(out, "Malloc", used, free, over,
|
||||
stats.perGen[SMGenNum_Malloc].totalAllocated,
|
||||
stats.perGen[SMGenNum_Malloc].totalRequested,
|
||||
0);
|
||||
used = stats.total.amountUsed;
|
||||
free = stats.total.amountFree;
|
||||
over = stats.total.overhead;
|
||||
if (detailed) {
|
||||
sm_DumpStat(out, " System", 0, 0, stats.systemOverhead, 0, 0, 0);
|
||||
}
|
||||
sm_DumpStat(out, "TOTAL", used, free, over,
|
||||
gcTotalAllocated + stats.perGen[SMGenNum_Malloc].totalAllocated,
|
||||
gcTotalRequested + stats.perGen[SMGenNum_Malloc].totalRequested,
|
||||
0);
|
||||
fprintf(out, "----------------------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
#endif /* SM_STATS */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_SetCollectThresholds(PRUword gen0Threshold, PRUword gen1Threshold,
|
||||
PRUword gen2Threshold, PRUword gen3Threshold,
|
||||
PRUword staticGenThreshold)
|
||||
{
|
||||
if (gen0Threshold != 0)
|
||||
sm_Heap.gen[SMGenNum_Freshman].allocThreshold = gen0Threshold;
|
||||
if (gen1Threshold != 0)
|
||||
sm_Heap.gen[SMGenNum_Sophomore].allocThreshold = gen1Threshold;
|
||||
if (gen2Threshold != 0)
|
||||
sm_Heap.gen[SMGenNum_Junior].allocThreshold = gen2Threshold;
|
||||
if (gen3Threshold != 0)
|
||||
sm_Heap.gen[SMGenNum_Senior].allocThreshold = gen3Threshold;
|
||||
if (staticGenThreshold != 0)
|
||||
sm_Heap.gen[SMGenNum_Static].allocThreshold = staticGenThreshold;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef SM_VERIFY
|
||||
|
||||
#if SM_VERIFY == 2
|
||||
|
||||
static void
|
||||
sm_VerifyRange(SMObject** refs, PRUword count)
|
||||
{
|
||||
PRUword i;
|
||||
for (i = 0; i < count; i++) {
|
||||
SMObject* obj = refs[i];
|
||||
if (obj) {
|
||||
SMObjStruct* dobj = SM_DECRYPT_OBJECT(obj);
|
||||
SMPageDesc* pd = SM_OBJECT_PAGEDESC(dobj);
|
||||
SMObjDesc* od = SM_OBJECT_HEADER_FROM_PAGEDESC(dobj, pd);
|
||||
SM_ASSERT(SM_OBJDESC_IS_MARKED(od));
|
||||
SM_ASSERT(SM_OBJECT_CLASS(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern SMPool sm_MallocPool;
|
||||
|
||||
void
|
||||
sm_VerifyHeap(void)
|
||||
{
|
||||
SMGenNum genNum;
|
||||
SMPageCount i;
|
||||
|
||||
//SM_ACQUIRE_MONITORS();
|
||||
//PR_SuspendAll();
|
||||
|
||||
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
|
||||
genNum = SM_PAGEDESC_GEN(pd);
|
||||
if (genNum == SMGenNum_Free) {
|
||||
SM_ASSERT(pd->next == NULL);
|
||||
SM_ASSERT(pd->objTable == NULL);
|
||||
SM_ASSERT(pd->allocCount == 0);
|
||||
SM_ASSERT(pd->largeObjDesc.flags == 0);
|
||||
}
|
||||
else {
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMPageDesc* pd2;
|
||||
SMSmallObjCount count = 0;
|
||||
SMSmallObjCount j;
|
||||
SMPool* pool = (genNum == SMGenNum_Malloc)
|
||||
? &sm_MallocPool
|
||||
: &sm_Heap.gen[genNum].pool;
|
||||
|
||||
if (bucket == SM_LARGE_OBJECT_BUCKET) {
|
||||
SM_ASSERT(pd->objTable == &pd->largeObjDesc);
|
||||
SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(pd));
|
||||
SM_ASSERT(pd->allocCount != 0);
|
||||
pd2 = pd;
|
||||
count = pd->allocCount;
|
||||
while (--count > 0) {
|
||||
i++;
|
||||
pd2++;
|
||||
SM_ASSERT(SM_PAGEDESC_GEN(pd2) == genNum);
|
||||
SM_ASSERT(SM_PAGEDESC_BUCKET(pd2) == bucket);
|
||||
SM_ASSERT(SM_PAGEDESC_LARGE_OBJECT_START(pd2) == pd);
|
||||
SM_ASSERT(pd2->objTable == &pd->largeObjDesc);
|
||||
SM_ASSERT(!SM_PAGEDESC_IS_LARGE_OBJECT_START(pd2));
|
||||
}
|
||||
}
|
||||
else {
|
||||
SMObjDesc* od = pd->objTable;
|
||||
for (j = 0; j < objsPerPage; j++) {
|
||||
if (SM_OBJDESC_IS_ALLOCATED(od)) {
|
||||
SM_ASSERT((od->flags & SM_OBJDESC_STATE_MASK) == SMObjectState_Marked);
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
SM_ASSERT(SM_OBJDESC_IS_FREE(od));
|
||||
}
|
||||
od++;
|
||||
}
|
||||
SM_ASSERT(pd->allocCount == count);
|
||||
pd2 = pool->sweepList[bucket].sweepPage;
|
||||
while (pd2 != NULL) {
|
||||
if (pd2 == pd) {
|
||||
SM_ASSERT(count <= objsPerPage);
|
||||
goto next;
|
||||
}
|
||||
pd2 = pd2->next;
|
||||
}
|
||||
/* If we get here, there wasn't a pagedesc in the sweep list.
|
||||
Figure out if it's somewhere else. */
|
||||
{
|
||||
SMGenNum g;
|
||||
for (g = SMGenNum_Freshman; g <= SMGenNum_Malloc; g++) {
|
||||
SMBucket b;
|
||||
for (b = 0; b < SM_ALLOC_BUCKETS; b++) {
|
||||
SMPool* pool = (g == SMGenNum_Malloc)
|
||||
? &sm_MallocPool
|
||||
: &sm_Heap.gen[g].pool;
|
||||
SMPageDesc* pd2 = pool->sweepList[b].sweepPage;
|
||||
while (pd2 != NULL) {
|
||||
if (!(g == genNum && b == bucket)) {
|
||||
SM_ASSERT(pd != pd2);
|
||||
}
|
||||
pd2 = pd2->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If the page desc wasn't found anywhere, then it better be full. */
|
||||
SM_ASSERT(count == objsPerPage);
|
||||
next:;
|
||||
/* continue */
|
||||
}
|
||||
|
||||
#if SM_VERIFY == 2
|
||||
/* Verify that the gc objects on the page have all their fields marked */
|
||||
if (SM_IS_GC_SPACE(genNum)) {
|
||||
SMObjDesc* od = pd->objTable;
|
||||
SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd);
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
for (j = 0; j < objsPerPage; j++) {
|
||||
if (SM_OBJDESC_IS_MARKED(od)) {
|
||||
SMClass* clazz = SM_OBJECT_CLASS(obj);
|
||||
switch (SM_CLASS_KIND(clazz)) {
|
||||
case SMClassKind_ObjectClass: {
|
||||
SMFieldDesc* fieldDesc = SM_CLASS_GET_INST_FIELD_DESCS(clazz);
|
||||
PRUint16 k, fdCount = SM_CLASS_GET_INST_FIELDS_COUNT(clazz);
|
||||
for (k = 0; k < fdCount; k++) {
|
||||
PRWord offset = fieldDesc[k].offset;
|
||||
PRUword count = fieldDesc[k].count;
|
||||
SMObject** fieldRefs = &SM_OBJECT_FIELDS(obj)[offset];
|
||||
sm_VerifyRange(fieldRefs, count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMClassKind_ArrayClass: {
|
||||
if (SM_IS_OBJECT_ARRAY_CLASS(clazz)) {
|
||||
PRUword size = SM_ARRAY_SIZE(obj);
|
||||
SMObject** elementRefs = SM_ARRAY_ELEMENTS(obj);
|
||||
sm_VerifyRange(elementRefs, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
obj = (SMObjStruct*)((char*)obj + objSize);
|
||||
od++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* also verify that all the sweep lists are consistent */
|
||||
for (genNum = SMGenNum_Freshman; genNum <= SMGenNum_Static; genNum++) {
|
||||
SMBucket bucket;
|
||||
for (bucket = 0; bucket < SM_ALLOC_BUCKETS; bucket++) {
|
||||
SMSweepList* sweepList = &sm_Heap.gen[genNum].pool.sweepList[bucket];
|
||||
SMPageDesc* pd = sweepList->sweepPage;
|
||||
SMSmallObjCount i;
|
||||
for (i = 0; i < sweepList->sweepIndex; i++) {
|
||||
SM_ASSERT(!SM_OBJDESC_IS_FREE(&pd->objTable[i]));
|
||||
}
|
||||
for (; pd != NULL; pd = pd->next) {
|
||||
SM_ASSERT(SM_PAGEDESC_BUCKET(pd) == bucket);
|
||||
SM_ASSERT(SM_PAGEDESC_GEN(pd) == genNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//SM_RELEASE_MONITORS();
|
||||
//PR_ResumeAll();
|
||||
}
|
||||
|
||||
#endif /* SM_VERIFY */
|
||||
|
||||
/******************************************************************************/
|
100
ef/gc/src/smobj.c
Normal file
100
ef/gc/src/smobj.c
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smobj.h"
|
||||
#include "smgen.h"
|
||||
#include "smheap.h"
|
||||
|
||||
PRUint8 sm_ClassKindSize[SM_CLASS_KIND_COUNT] = {
|
||||
/* Primitive class kinds: */
|
||||
0, /* SMClassKind_VoidClass */
|
||||
sizeof(PRInt8), /* SMClassKind_BooleanClass */
|
||||
sizeof(PRInt8), /* SMClassKind_ByteClass */
|
||||
sizeof(PRInt16), /* SMClassKind_CharClass */
|
||||
sizeof(PRInt16), /* SMClassKind_ShortClass */
|
||||
sizeof(PRInt32), /* SMClassKind_IntClass */
|
||||
sizeof(PRInt64), /* SMClassKind_LongClass */
|
||||
sizeof(float), /* SMClassKind_FloatClass */
|
||||
sizeof(double), /* SMClassKind_DoubleClass */
|
||||
/* Structured class kinds: */
|
||||
sizeof(SMArray*), /* SMClassKind_ArrayClass */
|
||||
sizeof(SMObject*), /* SMClassKind_ObjectClass */
|
||||
sizeof(SMObject*) /* SMClassKind_InterfaceClass */
|
||||
};
|
||||
|
||||
SM_IMPLEMENT_ENSURE(SMObject)
|
||||
SM_IMPLEMENT_ENSURE(SMObjStruct)
|
||||
SM_IMPLEMENT_ENSURE(SMArray)
|
||||
SM_IMPLEMENT_ENSURE(SMArrayStruct)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_InitObjectClass(SMObjectClass* clazz, SMObjectClass* metaClass,
|
||||
PRUword classSize, PRUword instanceSize,
|
||||
PRUint16 nInstFieldDescs, PRUint16 nWeakFieldDescs)
|
||||
{
|
||||
SMBucket bucket;
|
||||
|
||||
SM_ASSERT(classSize <= (1 << (sizeof(PRUint16) * 8)));
|
||||
|
||||
clazz->inherit.object.clazz = (SMClass*)metaClass;
|
||||
SM_GET_ALLOC_BUCKET(bucket, instanceSize);
|
||||
SM_CLASS_INIT_INFO(clazz, bucket, SMClassKind_ObjectClass, 0);
|
||||
clazz->data.staticFields = NULL;
|
||||
clazz->data.instFieldDescs.offset = (PRUint16)classSize;
|
||||
clazz->data.instFieldDescs.count = nInstFieldDescs;
|
||||
clazz->data.weakFieldDescs.offset =
|
||||
(PRUint16)classSize + nInstFieldDescs * sizeof(SMFieldDesc);
|
||||
clazz->data.weakFieldDescs.count = nWeakFieldDescs;
|
||||
clazz->data.instanceSize = instanceSize;
|
||||
clazz->data.finalize = NULL;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_InitArrayClass(SMArrayClass* clazz, SMObjectClass* metaClass,
|
||||
SMClass* elementClass)
|
||||
{
|
||||
clazz->inherit.object.clazz = (SMClass*)metaClass;
|
||||
SM_CLASS_INIT_INFO(clazz, 0, SMClassKind_ArrayClass, 0);
|
||||
clazz->data.elementClass = elementClass;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_InitPrimClass(SMPrimClass* clazz, SMObjectClass* metaClass,
|
||||
SMClassKind primKind)
|
||||
{
|
||||
clazz->inherit.object.clazz = (SMClass*)metaClass;
|
||||
SM_CLASS_INIT_INFO(clazz, 0, primKind, 0);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
670
ef/gc/src/smpage.c
Normal file
670
ef/gc/src/smpage.c
Normal file
@ -0,0 +1,670 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smpage.h"
|
||||
#include "sm.h"
|
||||
|
||||
#if defined(XP_PC)
|
||||
#include <windows.h>
|
||||
#elif defined(XP_MAC)
|
||||
#include <Types.h>
|
||||
#include <Memory.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
SMPageMgr sm_PageMgr; /* _the_ global page manager */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
struct SMClusterDesc {
|
||||
SMClusterDesc* next; /* Link to next cluster of free pages */
|
||||
SMPage* addr; /* First page in cluster of free pages */
|
||||
SMPageCount nPages; /* Total size of cluster of free pages in bytes */
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static SMClusterDesc* unusedClusterDescs;
|
||||
|
||||
#define SM_CLUSTERDESC_CLUMPSIZE 1
|
||||
|
||||
static void
|
||||
sm_DeleteFreeClusterDesc(SMPageMgr* pm, SMClusterDesc *desc)
|
||||
{
|
||||
#if 1
|
||||
desc->next = unusedClusterDescs;
|
||||
unusedClusterDescs = desc;
|
||||
#else
|
||||
SM_Free(desc);
|
||||
#endif
|
||||
}
|
||||
|
||||
static SMClusterDesc*
|
||||
sm_NewFreeClusterDesc(SMPageMgr* pm)
|
||||
{
|
||||
SMClusterDesc *desc = unusedClusterDescs;
|
||||
if (desc)
|
||||
unusedClusterDescs = desc->next;
|
||||
else {
|
||||
/* Allocate a clump of cluster records at once, and link all except
|
||||
the first onto the list of unusedClusterDescs */
|
||||
desc = (SMClusterDesc*)SM_Malloc(SM_CLUSTERDESC_CLUMPSIZE * sizeof(SMClusterDesc));
|
||||
if (desc) {
|
||||
SMClusterDesc* desc2 = desc + (SM_CLUSTERDESC_CLUMPSIZE - 1);
|
||||
while (desc2 != desc) {
|
||||
sm_DeleteFreeClusterDesc(pm, desc2--);
|
||||
}
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
/* Search the freeClusters looking for the first cluster of consecutive free
|
||||
pages that is at least size bytes long. If there is one, remove these pages
|
||||
from the free page list and return their address; if not, return nil. */
|
||||
static SMPage*
|
||||
sm_AllocClusterFromFreeList(SMPageMgr* pm, PRUword nPages)
|
||||
{
|
||||
SMClusterDesc **p = &pm->freeClusters;
|
||||
SMClusterDesc *desc;
|
||||
while ((desc = *p) != NULL) {
|
||||
if (desc->nPages >= nPages) {
|
||||
SMPage* addr = desc->addr;
|
||||
if (desc->nPages == nPages) {
|
||||
*p = desc->next;
|
||||
sm_DeleteFreeClusterDesc(pm, desc);
|
||||
}
|
||||
else {
|
||||
desc->addr += nPages;
|
||||
desc->nPages -= nPages;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
p = &desc->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add the segment to the SMClusterDesc list, coalescing it with any
|
||||
clusters already in the list when possible. */
|
||||
static void
|
||||
sm_AddClusterToFreeList(SMPageMgr* pm, SMPage* addr, PRWord nPages)
|
||||
{
|
||||
SMClusterDesc **p = &pm->freeClusters;
|
||||
SMClusterDesc *desc;
|
||||
SMClusterDesc *newDesc;
|
||||
|
||||
while ((desc = *p) != NULL) {
|
||||
if (desc->addr + desc->nPages == addr) {
|
||||
/* Coalesce with the previous cluster. */
|
||||
SMClusterDesc *next = desc->next;
|
||||
|
||||
desc->nPages += nPages;
|
||||
if (next && next->addr == addr + nPages) {
|
||||
/* We can coalesce with both the previous and the next cluster. */
|
||||
desc->nPages += next->nPages;
|
||||
desc->next = next->next;
|
||||
sm_DeleteFreeClusterDesc(pm, next);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (desc->addr == addr + nPages) {
|
||||
/* Coalesce with the next cluster. */
|
||||
desc->addr -= nPages;
|
||||
desc->nPages += nPages;
|
||||
return;
|
||||
}
|
||||
if (desc->addr > addr) {
|
||||
PR_ASSERT(desc->addr > addr + nPages);
|
||||
break;
|
||||
}
|
||||
PR_ASSERT(desc->addr + desc->nPages < addr);
|
||||
p = &desc->next;
|
||||
}
|
||||
newDesc = sm_NewFreeClusterDesc(pm);
|
||||
/* In the unlikely event that this malloc fails, we drop the free cluster
|
||||
on the floor. The only consequence is that the memory mapping table
|
||||
becomes slightly larger. */
|
||||
if (newDesc) {
|
||||
newDesc->next = desc;
|
||||
newDesc->addr = addr;
|
||||
newDesc->nPages = nPages;
|
||||
*p = newDesc;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef xDEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef XP_PC
|
||||
#define OutputDebugString(x) puts(x)
|
||||
#endif
|
||||
|
||||
static void
|
||||
sm_VerifyClusters(SMPageMgr* pm, SMPageCount nPagesDelta)
|
||||
{
|
||||
static PRWord expectedPagesUsed = 0;
|
||||
SMPageCount calculatedPagesUsed;
|
||||
SMPage* lastDescEnd = 0;
|
||||
SMClusterDesc* desc;
|
||||
char str[256];
|
||||
expectedPagesUsed += nPagesDelta;
|
||||
calculatedPagesUsed = pm->boundary - pm->memoryBase;
|
||||
sprintf(str, "[Clusters: %p", pm->memoryBase);
|
||||
OutputDebugString(str);
|
||||
for (desc = pm->freeClusters; desc; desc = desc->next) {
|
||||
PR_ASSERT(desc->addr > lastDescEnd);
|
||||
calculatedPagesUsed -= desc->nPages;
|
||||
lastDescEnd = desc->addr + desc->nPages;
|
||||
sprintf(str, "..%p, %p", desc->addr-1, desc->addr + desc->nPages);
|
||||
OutputDebugString(str);
|
||||
}
|
||||
sprintf(str, "..%p]\n", pm->boundary);
|
||||
OutputDebugString(str);
|
||||
PR_ASSERT(lastDescEnd < pm->boundary);
|
||||
PR_ASSERT(calculatedPagesUsed == expectedPagesUsed);
|
||||
}
|
||||
|
||||
#define SM_VERIFYCLUSTERS(pm, nPagesDelta) sm_VerifyClusters(pm, nPagesDelta)
|
||||
|
||||
#else /* !DEBUG */
|
||||
|
||||
#define SM_VERIFYCLUSTERS(pm, nPagesDelta) /* no-op */
|
||||
|
||||
#endif /* !DEBUG */
|
||||
|
||||
/*******************************************************************************
|
||||
* Machine-dependent stuff
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(XP_PC)
|
||||
|
||||
#define GC_VMBASE 0x40000000 /* XXX move */
|
||||
#define GC_VMLIMIT 0x0FFFFFFF
|
||||
|
||||
#elif defined(XP_MAC)
|
||||
|
||||
#define SM_MAC_SEGMENT_SIZE
|
||||
#define SM_MAC_SEGMENT_COUNT
|
||||
|
||||
struct SMSegmentDesc {
|
||||
Handle handle;
|
||||
SMPage* firstPage;
|
||||
SMPage* lastPage;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static PRStatus
|
||||
sm_InitPages(SMPageMgr* pm, SMPageCount minPages, SMPageCount maxPages)
|
||||
{
|
||||
#if defined(XP_PC)
|
||||
|
||||
SMPage* addr = NULL;
|
||||
SMPageCount size = maxPages;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* first try to place the heap at a well-known address for debugging */
|
||||
addr = (SMPage*)VirtualAlloc((void*)GC_VMBASE, size << SM_PAGE_BITS,
|
||||
MEM_RESERVE, PAGE_READWRITE);
|
||||
#endif
|
||||
while (addr == NULL) {
|
||||
/* let the system place the heap */
|
||||
addr = (SMPage*)VirtualAlloc(0, size << SM_PAGE_BITS,
|
||||
MEM_RESERVE, PAGE_READWRITE);
|
||||
if (addr == NULL) {
|
||||
size--;
|
||||
if (size < minPages) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
SM_ASSERT(SM_IS_ALIGNED(addr, SM_PAGE_BITS));
|
||||
pm->memoryBase = addr;
|
||||
pm->pageCount = size;
|
||||
pm->boundary = addr;
|
||||
pm->minPage = SM_PAGE_NUMBER(addr);
|
||||
|
||||
return PR_SUCCESS;
|
||||
|
||||
#elif defined(XP_MAC)
|
||||
|
||||
OSErr err;
|
||||
void* seg;
|
||||
void* segLimit;
|
||||
Handle h;
|
||||
PRUword segSize = (minPages + 1) * SM_PAGE_SIZE;
|
||||
SMPage* firstPage;
|
||||
SMPage* lastPage;
|
||||
SMSegmentDesc* segTable;
|
||||
int segTableCount, otherCount;
|
||||
|
||||
h = TempNewHandle(segSize, &err);
|
||||
if (err || h == NULL) goto fail;
|
||||
MoveHHi(h);
|
||||
TempHLock(h, &err);
|
||||
if (err) goto fail;
|
||||
seg = *h;
|
||||
segLimit = (void*)((char*)seg + segSize);
|
||||
firstPage = SM_PAGE_ROUNDUP(seg);
|
||||
lastPage = SM_PAGE_ROUNDDN(((char*)seg + segSize));
|
||||
|
||||
/* Put the segment table in the otherwise wasted space at one
|
||||
end of the segment. We'll put it at which ever end is bigger. */
|
||||
segTable = (SMSegmentDesc*)seg;
|
||||
segTableCount = ((char*)firstPage - (char*)seg) / sizeof(SMSegmentDesc);
|
||||
otherCount = ((char*)segLimit - (char*)lastPage) / sizeof(SMSegmentDesc);
|
||||
if (otherCount > segTableCount) {
|
||||
segTable = (SMSegmentDesc*)lastPage;
|
||||
segTableCount = otherCount;
|
||||
}
|
||||
else if (segTableCount == 0) {
|
||||
segTable = (SMSegmentDesc*)firstPage;
|
||||
firstPage++;
|
||||
segTableCount = SM_PAGE_SIZE / sizeof(SMSegmentDesc);
|
||||
}
|
||||
SM_ASSERT(segTableCount > 0);
|
||||
pm->segTable = segTable;
|
||||
pm->segTableCount = segTableCount;
|
||||
|
||||
segTable[0].handle = h;
|
||||
segTable[0].firstPage = firstPage;
|
||||
segTable[0].lastPage = lastPage;
|
||||
|
||||
/* XXX hack for now -- just one segment */
|
||||
pm->memoryBase = firstPage;
|
||||
pm->boundary = firstPage;
|
||||
pm->minPage = SM_PAGE_NUMBER(firstPage);
|
||||
pm->pageCount = lastPage - firstPage;
|
||||
|
||||
return PR_SUCCESS;
|
||||
|
||||
fail:
|
||||
if (h) {
|
||||
TempDisposeHandle(h, &err);
|
||||
}
|
||||
return PR_FAILURE;
|
||||
|
||||
#else
|
||||
# error "write me"
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
sm_FiniPages(SMPageMgr* pm)
|
||||
{
|
||||
#if defined(XP_PC)
|
||||
|
||||
BOOL ok;
|
||||
ok = VirtualFree((void*)pm->memoryBase, 0, MEM_RELEASE);
|
||||
SM_ASSERT(ok);
|
||||
pm->memoryBase = NULL;
|
||||
pm->pageCount = 0;
|
||||
PR_DestroyMonitor(pm->monitor);
|
||||
pm->monitor == NULL;
|
||||
|
||||
#elif defined(XP_MAC)
|
||||
|
||||
#else
|
||||
# error "write me"
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Page Manager
|
||||
******************************************************************************/
|
||||
|
||||
PRStatus
|
||||
sm_InitPageMgr(SMPageMgr* pm, SMPageCount minPages, SMPageCount maxPages)
|
||||
{
|
||||
PRStatus status;
|
||||
PRUword pageTableSize, pageTablePages;
|
||||
SMSmallObjCount i;
|
||||
PRUword allocPages = minPages;
|
||||
|
||||
memset(pm, 0, sizeof(pm));
|
||||
|
||||
pm->monitor = PR_NewMonitor();
|
||||
if (pm->monitor == NULL)
|
||||
return PR_FAILURE;
|
||||
pm->alreadyLocked = PR_FALSE;
|
||||
|
||||
pageTableSize = minPages * sizeof(SMPageDesc);
|
||||
pageTablePages = SM_PAGE_COUNT(pageTableSize);
|
||||
|
||||
allocPages += pageTablePages;
|
||||
if (allocPages > maxPages) {
|
||||
pageTableSize = maxPages * sizeof(SMPageDesc);
|
||||
pageTablePages = SM_PAGE_COUNT(pageTableSize);
|
||||
allocPages = maxPages;
|
||||
}
|
||||
|
||||
status = sm_InitPages(pm, allocPages, allocPages);
|
||||
if (status != PR_SUCCESS)
|
||||
return status;
|
||||
|
||||
/* make sure these got set */
|
||||
SM_ASSERT(pm->memoryBase);
|
||||
SM_ASSERT(pm->boundary);
|
||||
SM_ASSERT(pm->minPage);
|
||||
|
||||
pm->freeClusters = NULL;
|
||||
pm->heapBase = pm->memoryBase + pageTablePages;
|
||||
pm->heapPageCount = pm->pageCount - pageTablePages;
|
||||
|
||||
/* Initialize page table */
|
||||
pm->pageTableMem = (SMPageDesc*)sm_NewCluster(pm, pageTablePages);
|
||||
if (pm->pageTableMem == NULL) {
|
||||
sm_FiniPageMgr(pm);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
pm->pageTable = pm->pageTableMem - (pm->minPage + pageTablePages);
|
||||
for (i = 0; i < pm->heapPageCount; i++) {
|
||||
SMPageDesc* pd = &pm->pageTableMem[i];
|
||||
SM_PAGEDESC_INIT(pd, 0, SMGenNum_Free, NULL, 0);
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
#if defined(XP_PC) && defined(DEBUG)
|
||||
|
||||
static PRUword sm_LastPageAllocTries = 0;
|
||||
static PRUword sm_LastPageAllocHits = 0;
|
||||
static PRUword sm_LastPageFreeTries = 0;
|
||||
static PRUword sm_LastPageFreeHits = 0;
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
sm_FiniPageMgr(SMPageMgr* pm)
|
||||
{
|
||||
#if 0
|
||||
if (pm->pageTableMem) {
|
||||
sm_DestroyCluster(pm, (SMPage*)pm->pageTableMem,
|
||||
SM_PAGE_COUNT(pm->heapPageCount));
|
||||
}
|
||||
#endif
|
||||
#if defined(XP_PC) && defined(DEBUG)
|
||||
if (sm_DebugOutput) {
|
||||
fprintf(sm_DebugOutput, "Page Manager Cache: alloc hits: %u/%u %u%%, free hits: %u/%u %u%%\n",
|
||||
sm_LastPageAllocHits, sm_LastPageAllocTries,
|
||||
(sm_LastPageAllocHits * 100 / sm_LastPageAllocTries),
|
||||
sm_LastPageFreeHits, sm_LastPageFreeTries,
|
||||
(sm_LastPageFreeHits * 100 / sm_LastPageFreeTries));
|
||||
}
|
||||
#endif
|
||||
sm_FiniPages(pm);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef XP_PC
|
||||
|
||||
#define SM_PAGE_HYSTERESIS /* undef if you want to compare */
|
||||
#ifdef SM_PAGE_HYSTERESIS
|
||||
|
||||
/* one-page hysteresis */
|
||||
static void* sm_LastPageFreed = NULL;
|
||||
static PRUword sm_LastPageFreedSize = 0;
|
||||
static void* sm_LastPageTemp = NULL;
|
||||
static PRUword sm_LastPageTempSize = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
//#define SM_COMMIT_TRACE
|
||||
|
||||
static void*
|
||||
SM_COMMIT_CLUSTER(void* addr, PRUword size)
|
||||
{
|
||||
sm_LastPageAllocTries++;
|
||||
if (sm_LastPageFreed == (void*)(addr) && sm_LastPageFreedSize == (size)) {
|
||||
#ifdef SM_COMMIT_TRACE
|
||||
char buf[64];
|
||||
PR_snprintf(buf, sizeof(buf), "lalloc %p %u\n",
|
||||
sm_LastPageFreed, sm_LastPageFreedSize);
|
||||
OutputDebugString(buf);
|
||||
#endif
|
||||
DBG_MEMSET(sm_LastPageFreed, SM_PAGE_ALLOC_PATTERN, sm_LastPageFreedSize);
|
||||
sm_LastPageTemp = sm_LastPageFreed;
|
||||
sm_LastPageFreed = NULL;
|
||||
sm_LastPageFreedSize = 0;
|
||||
sm_LastPageAllocHits++;
|
||||
return sm_LastPageTemp;
|
||||
}
|
||||
else {
|
||||
/* If the cached pages intersect the current request, we lose.
|
||||
Just free the cached request instead of trying to split it up. */
|
||||
if (sm_LastPageFreed &&
|
||||
SM_OVERLAPPING((char*)sm_LastPageFreed, ((char*)sm_LastPageFreed + sm_LastPageFreedSize),
|
||||
(char*)(addr), ((char*)(addr) + (size)))
|
||||
// ((char*)sm_LastPageFreed < ((char*)(addr) + (size))
|
||||
// && ((char*)sm_LastPageFreed + sm_LastPageFreedSize) > (char*)(addr))
|
||||
) {
|
||||
#ifdef SM_COMMIT_TRACE
|
||||
char buf[64];
|
||||
PR_snprintf(buf, sizeof(buf), "valloc %p %u (vfree %p %u last=%u:%u req=%u:%u)\n",
|
||||
addr, size,
|
||||
sm_LastPageFreed, sm_LastPageFreedSize,
|
||||
(char*)sm_LastPageFreed, ((char*)sm_LastPageFreed + sm_LastPageFreedSize),
|
||||
(char*)(addr), ((char*)(addr) + (size)));
|
||||
OutputDebugString(buf);
|
||||
#endif
|
||||
VirtualFree(sm_LastPageFreed, sm_LastPageFreedSize, MEM_DECOMMIT);
|
||||
sm_LastPageFreed = NULL;
|
||||
sm_LastPageFreedSize = 0;
|
||||
sm_LastPageFreeHits--; /* lost after all */
|
||||
}
|
||||
else {
|
||||
#ifdef SM_COMMIT_TRACE
|
||||
char buf[64];
|
||||
PR_snprintf(buf, sizeof(buf), "valloc %p %u (skipping %p %u)\n",
|
||||
addr, size,
|
||||
sm_LastPageFreed, sm_LastPageFreedSize);
|
||||
OutputDebugString(buf);
|
||||
#endif
|
||||
}
|
||||
return VirtualAlloc((void*)(addr), (size), MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
SM_DECOMMIT_CLUSTER(void* addr, PRUword size)
|
||||
{
|
||||
sm_LastPageFreeTries++;
|
||||
SM_ASSERT(sm_LastPageFreed != (void*)(addr));
|
||||
if (sm_LastPageFreed) {
|
||||
/* If we've already got a cached page, just keep it. Heuristically,
|
||||
this tends to give us a higher hit rate because of the order in
|
||||
which pages are decommitted. */
|
||||
#ifdef SM_COMMIT_TRACE
|
||||
char buf[64];
|
||||
PR_snprintf(buf, sizeof(buf), "vfree %p %u (cached %p %u)\n",
|
||||
addr, size, sm_LastPageFreed, sm_LastPageFreedSize);
|
||||
OutputDebugString(buf);
|
||||
#endif
|
||||
return VirtualFree(addr, size, MEM_DECOMMIT);
|
||||
}
|
||||
sm_LastPageFreed = (void*)(addr);
|
||||
sm_LastPageFreedSize = (size);
|
||||
DBG_MEMSET(sm_LastPageFreed, SM_PAGE_FREE_PATTERN, sm_LastPageFreedSize);
|
||||
#ifdef SM_COMMIT_TRACE
|
||||
{
|
||||
char buf[64];
|
||||
PR_snprintf(buf, sizeof(buf), "lfree %p %u\n",
|
||||
sm_LastPageFreed, sm_LastPageFreedSize);
|
||||
OutputDebugString(buf);
|
||||
}
|
||||
#endif
|
||||
sm_LastPageFreeHits++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* !DEBUG */
|
||||
|
||||
#define SM_COMMIT_CLUSTER(addr, size) \
|
||||
(SM_ASSERT((void*)(addr) != NULL), \
|
||||
((sm_LastPageFreed == (void*)(addr) && sm_LastPageFreedSize == (size)) \
|
||||
? (DBG_MEMSET(sm_LastPageFreed, SM_PAGE_ALLOC_PATTERN, sm_LastPageFreedSize), \
|
||||
sm_LastPageTemp = sm_LastPageFreed, \
|
||||
sm_LastPageFreed = NULL, \
|
||||
sm_LastPageFreedSize = 0, \
|
||||
sm_LastPageTemp) \
|
||||
: (((sm_LastPageFreed && \
|
||||
((char*)sm_LastPageFreed < ((char*)(addr) + (size)) \
|
||||
&& ((char*)sm_LastPageFreed + sm_LastPageFreedSize) > (char*)(addr))) \
|
||||
? (VirtualFree(sm_LastPageFreed, sm_LastPageFreedSize, MEM_DECOMMIT), \
|
||||
sm_LastPageFreed = NULL, \
|
||||
sm_LastPageFreedSize = 0) \
|
||||
: ((void)0)), \
|
||||
VirtualAlloc((void*)(addr), (size), MEM_COMMIT, PAGE_READWRITE)))) \
|
||||
|
||||
#define SM_DECOMMIT_CLUSTER(addr, size) \
|
||||
(SM_ASSERT(sm_LastPageFreed != (void*)(addr)), \
|
||||
(sm_LastPageFreed \
|
||||
? (VirtualFree(addr, size, MEM_DECOMMIT)) \
|
||||
: (sm_LastPageFreed = (addr), \
|
||||
sm_LastPageFreedSize = (size), \
|
||||
DBG_MEMSET(sm_LastPageFreed, SM_PAGE_FREE_PATTERN, sm_LastPageFreedSize), \
|
||||
1))) \
|
||||
|
||||
#endif /* !DEBUG */
|
||||
|
||||
#else /* !SM_PAGE_HYSTERESIS */
|
||||
|
||||
#define SM_COMMIT_CLUSTER(addr, size) \
|
||||
VirtualAlloc((void*)(addr), (size), MEM_COMMIT, PAGE_READWRITE)
|
||||
|
||||
#define SM_DECOMMIT_CLUSTER(addr, size) \
|
||||
VirtualFree((void*)(addr), (size), MEM_DECOMMIT)
|
||||
|
||||
#endif /* !SM_PAGE_HYSTERESIS */
|
||||
|
||||
#else /* !XP_PC */
|
||||
|
||||
#define SM_COMMIT_CLUSTER(addr, size) (addr)
|
||||
#define SM_DECOMMIT_CLUSTER(addr, size) 1
|
||||
|
||||
#endif /* !XP_PC */
|
||||
|
||||
SMPage*
|
||||
sm_NewCluster(SMPageMgr* pm, SMPageCount nPages)
|
||||
{
|
||||
SMPage* addr;
|
||||
|
||||
SM_ASSERT(nPages > 0);
|
||||
if (!pm->alreadyLocked)
|
||||
PR_EnterMonitor(pm->monitor);
|
||||
else {
|
||||
SM_ASSERT(PR_InMonitor(pm->monitor));
|
||||
}
|
||||
addr = sm_AllocClusterFromFreeList(pm, nPages);
|
||||
if (!addr && pm->boundary + nPages <= pm->memoryBase + pm->pageCount) {
|
||||
addr = pm->boundary;
|
||||
pm->boundary += nPages;
|
||||
}
|
||||
if (addr) {
|
||||
/* Extend the mapping */
|
||||
SMPage* vaddr;
|
||||
PRUword size = nPages << SM_PAGE_BITS;
|
||||
SM_ASSERT(SM_IS_ALIGNED(addr, SM_PAGE_BITS));
|
||||
vaddr = (SMPage*)SM_COMMIT_CLUSTER((void*)addr, size);
|
||||
SM_VERIFYCLUSTERS(pm, nPages);
|
||||
if (addr) {
|
||||
PR_ASSERT(vaddr == addr);
|
||||
}
|
||||
else {
|
||||
sm_DestroyCluster(pm, addr, nPages);
|
||||
}
|
||||
DBG_MEMSET(addr, SM_PAGE_ALLOC_PATTERN, size);
|
||||
}
|
||||
if (!pm->alreadyLocked)
|
||||
PR_ExitMonitor(pm->monitor);
|
||||
return (SMPage*)addr;
|
||||
}
|
||||
|
||||
void
|
||||
sm_DestroyCluster(SMPageMgr* pm, SMPage* basePage, SMPageCount nPages)
|
||||
{
|
||||
int freeResult;
|
||||
PRUword size = nPages << SM_PAGE_BITS;
|
||||
|
||||
SM_ASSERT(nPages > 0);
|
||||
SM_ASSERT(SM_IS_ALIGNED(basePage, SM_PAGE_BITS));
|
||||
SM_ASSERT(pm->memoryBase <= basePage);
|
||||
SM_ASSERT(basePage + nPages <= pm->memoryBase + pm->pageCount);
|
||||
DBG_MEMSET(basePage, SM_PAGE_FREE_PATTERN, size);
|
||||
freeResult = SM_DECOMMIT_CLUSTER((void*)basePage, size);
|
||||
SM_ASSERT(freeResult);
|
||||
if (!pm->alreadyLocked)
|
||||
PR_EnterMonitor(pm->monitor);
|
||||
else {
|
||||
SM_ASSERT(PR_InMonitor(pm->monitor));
|
||||
}
|
||||
if (basePage + nPages == pm->boundary) {
|
||||
SMClusterDesc **p;
|
||||
SMClusterDesc *desc;
|
||||
/* We deallocated the last set of clusters. Move the boundary lower. */
|
||||
pm->boundary = basePage;
|
||||
/* The last free cluster might now be adjacent to the boundary; if so,
|
||||
move the boundary before that cluster and delete that cluster
|
||||
altogether. */
|
||||
p = &pm->freeClusters;
|
||||
while ((desc = *p) != NULL) {
|
||||
if (!desc->next && desc->addr + desc->nPages == pm->boundary) {
|
||||
*p = 0;
|
||||
pm->boundary = desc->addr;
|
||||
sm_DeleteFreeClusterDesc(pm, desc);
|
||||
}
|
||||
else {
|
||||
p = &desc->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
sm_AddClusterToFreeList(pm, basePage, nPages);
|
||||
}
|
||||
/* reset page descriptors */
|
||||
{
|
||||
SMPageDesc* pd = SM_OBJECT_PAGEDESC(basePage);
|
||||
SMPageCount i;
|
||||
for (i = 0; i < nPages; i++) {
|
||||
SM_PAGEDESC_INIT(pd, 0, SMGenNum_Free, NULL, 0);
|
||||
pd++;
|
||||
}
|
||||
}
|
||||
SM_VERIFYCLUSTERS(pm, -nPages);
|
||||
if (!pm->alreadyLocked)
|
||||
PR_ExitMonitor(pm->monitor);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
660
ef/gc/src/smpool.c
Normal file
660
ef/gc/src/smpool.c
Normal file
@ -0,0 +1,660 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smpool.h"
|
||||
#include <string.h>
|
||||
|
||||
SMSmallObjSize sm_ObjectSize[SM_ALLOC_BUCKETS];
|
||||
SMSmallObjCount sm_ObjectsPerPage[SM_ALLOC_BUCKETS];
|
||||
SMCompactProc sm_CompactHook;
|
||||
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
static SMSmallObjCount sm_ObjectSizeDivTableMem[SM_DIVTABLE_ENTRY_COUNT];
|
||||
SMSmallObjCount* sm_ObjectSizeDivTable[SM_ALLOC_BUCKETS];
|
||||
#endif
|
||||
|
||||
void
|
||||
sm_InitFastDivision(void)
|
||||
{
|
||||
SMBucket bucket;
|
||||
SMSmallObjSize objSize;
|
||||
SMSmallObjCount objsPerPage;
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
SMSmallObjCount* fill = sm_ObjectSizeDivTableMem;
|
||||
int i;
|
||||
#endif
|
||||
|
||||
/* Make sure each entry in sm_ObjectSizeDivTableMem
|
||||
is big enough for the page size chosen. */
|
||||
SM_ASSERT(SM_PAGE_REFS <= SM_MAX_VALUE(SMSmallObjCount));
|
||||
|
||||
objSize = SM_MIN_SMALLOBJECT_SIZE;
|
||||
for (bucket = 0; bucket < SM_POWER_OF_TWO_BUCKETS; bucket++) {
|
||||
sm_ObjectSize[bucket] = objSize;
|
||||
objsPerPage = SM_PAGE_SIZE / objSize;
|
||||
sm_ObjectsPerPage[bucket] = objsPerPage;
|
||||
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
/* We don't use sm_ObjectSizeDivTable for power-of-two
|
||||
sized buckets -- we'll shift instead (in the SM_DIV
|
||||
macro). */
|
||||
sm_ObjectSizeDivTable[bucket] = NULL;
|
||||
#endif
|
||||
objSize <<= 1;
|
||||
}
|
||||
objSize = (SM_MIN_SMALLOBJECT_SIZE * 3) / 2;
|
||||
for (; bucket < SM_ALLOC_BUCKETS - 1; bucket++) {
|
||||
sm_ObjectSize[bucket] = objSize;
|
||||
objsPerPage = SM_PAGE_SIZE / objSize;
|
||||
sm_ObjectsPerPage[bucket] = objsPerPage;
|
||||
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
sm_ObjectSizeDivTable[bucket] = fill;
|
||||
for (i = 0; i < SM_PAGE_REFS; i++) {
|
||||
*fill++ = (i << SM_REF_BITS) / objSize;
|
||||
}
|
||||
#endif
|
||||
objSize <<= 1;
|
||||
}
|
||||
sm_ObjectSize[SM_LARGE_OBJECT_BUCKET] = 0;
|
||||
sm_ObjectsPerPage[SM_LARGE_OBJECT_BUCKET] = 1;
|
||||
|
||||
#ifndef SM_NO_TABLE_DIVISION
|
||||
/* set up the last entry in the div table to handle large objects */
|
||||
sm_ObjectSizeDivTable[bucket] = fill;
|
||||
*fill++ = 0;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Make sure our fancy division stuff is working. */
|
||||
for (bucket = 0; bucket < SM_ALLOC_BUCKETS - 1; bucket++) {
|
||||
int i;
|
||||
for (i = 0; i < SM_PAGE_REFS; i++) {
|
||||
PRUword obj = i << SM_REF_BITS;
|
||||
PRUword objSize = sm_ObjectSize[bucket];
|
||||
SM_ASSERT(SM_DIV(obj, bucket) == obj / objSize);
|
||||
}
|
||||
}
|
||||
/* Make sure it works for large objects too */
|
||||
{
|
||||
int i;
|
||||
for (i = SM_PAGE_SIZE - 8; i < SM_PAGE_SIZE + 8; i++) {
|
||||
PRUword obj = i;
|
||||
PRUword value = SM_DIV(obj, SM_LARGE_OBJECT_BUCKET);
|
||||
SM_ASSERT(value == 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void*
|
||||
sm_PoolInitPage(SMPool* pool, SMGenNum genNum, SMBucket bucket,
|
||||
SMPage* page, SMObjDesc* objTable, SMSmallObjCount allocIndex)
|
||||
{
|
||||
void* obj = NULL;
|
||||
SMPageCount pageNum = SM_PAGE_NUMBER(page);
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTable[pageNum];
|
||||
SMSweepList* sweepList = &pool->sweepList[bucket];
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMSmallObjCount i;
|
||||
SMObjDesc* od;
|
||||
|
||||
SM_PAGEDESC_INIT(pd, bucket, genNum, objTable, allocIndex + 1);
|
||||
if (bucket >= SM_FIRST_MIDSIZE_BUCKET) {
|
||||
/* If we are dealing with inbetween sizes, then we must increment
|
||||
the number of objects per page to account for the extra unused
|
||||
space at the end of the page. We need an object descriptor for
|
||||
this in case a conservative pointer points to the space there. */
|
||||
objsPerPage++;
|
||||
}
|
||||
od = objTable;
|
||||
SM_UNROLLED_WHILE(objsPerPage, {
|
||||
SM_OBJDESC_SET_FREE(od++);
|
||||
});
|
||||
for (i = 0; i <= allocIndex; i++) {
|
||||
SM_OBJDESC_SET_ALLOCATED(&objTable[i]);
|
||||
}
|
||||
pd->allocCount = allocIndex + 1;
|
||||
|
||||
/* link page into free list */
|
||||
SM_ASSERT(pd->next == NULL);
|
||||
pd->next = sweepList->sweepPage;
|
||||
sweepList->sweepPage = pd;
|
||||
sweepList->sweepIndex = allocIndex + 1;
|
||||
|
||||
obj = (void*)((char*)page + allocIndex * objSize);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PRStatus
|
||||
sm_InitPool(SMPool* pool)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(pool, 0, sizeof(pool)); /* in case we fail */
|
||||
for (i = 0; i < SM_ALLOC_BUCKETS; i++) {
|
||||
SMSweepList* sweepList = &pool->sweepList[i];
|
||||
PRMonitor* monitor = PR_NewMonitor();
|
||||
if (monitor == NULL) {
|
||||
sm_FiniPool(pool);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
sweepList->monitor = monitor;
|
||||
sweepList->sweepPage = NULL;
|
||||
sweepList->sweepIndex = 0;
|
||||
}
|
||||
pool->allocAmount = 0;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
sm_FiniPool(SMPool* pool)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SM_ALLOC_BUCKETS; i++) {
|
||||
SMSweepList* sweepList = &pool->sweepList[i];
|
||||
if (sweepList->monitor) {
|
||||
PR_DestroyMonitor(sweepList->monitor);
|
||||
sweepList->monitor = NULL;
|
||||
sweepList->sweepPage = NULL;
|
||||
sweepList->sweepIndex = 0;
|
||||
}
|
||||
}
|
||||
pool->allocAmount = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void*
|
||||
sm_PoolAllocObj(SMPool* pool, SMBucket bucket)
|
||||
{
|
||||
SMSweepList* sweepList = &pool->sweepList[bucket];
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMPageDesc* pd;
|
||||
|
||||
/*SM_ASSERT(PR_InMonitor(sweepList->monitor)); XXX not when called during gc */
|
||||
|
||||
/* pages on the sweep list should never be large */
|
||||
SM_ASSERT(bucket != SM_LARGE_OBJECT_BUCKET);
|
||||
|
||||
/* lazy sweep -- start from where we left off, and only go until
|
||||
we find what we need */
|
||||
while ((pd = sweepList->sweepPage) != NULL) {
|
||||
SM_ASSERT(SM_PAGEDESC_BUCKET(pd) == bucket);
|
||||
SM_ASSERT(pd->allocCount != 0);
|
||||
while (sweepList->sweepIndex < objsPerPage) {
|
||||
SMSmallObjCount i = sweepList->sweepIndex++;
|
||||
SMObjDesc* od = &pd->objTable[i];
|
||||
if (SM_OBJDESC_IS_FREE(od)) {
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
void* obj = ((char*)SM_PAGEDESC_PAGE(pd) + i * objSize);
|
||||
SM_OBJDESC_SET_ALLOCATED(od);
|
||||
if (++pd->allocCount == objsPerPage) {
|
||||
/* if this allocation caused this page to become
|
||||
full, remove the page from the sweep list */
|
||||
sweepList->sweepPage = pd->next;
|
||||
pd->next = NULL;
|
||||
sweepList->sweepIndex = 0;
|
||||
}
|
||||
SM_POOL_VERIFY_PAGE(pool, bucket, pd);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
sweepList->sweepPage = pd->next;
|
||||
pd->next = NULL;
|
||||
sweepList->sweepIndex = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
sm_PoolRemovePage(SMPool* pool, SMBucket bucket, SMPageDesc* freePd)
|
||||
{
|
||||
SMPageDesc** pd2Addr = &pool->sweepList[bucket].sweepPage;
|
||||
SMPage* page = SM_PAGEDESC_PAGE(freePd);
|
||||
SMPageDesc* pd2;
|
||||
|
||||
while ((pd2 = *pd2Addr) != NULL) {
|
||||
if (pd2 == freePd) {
|
||||
*pd2Addr = pd2->next;
|
||||
SM_POOL_DESTROY_PAGE(pool, freePd);
|
||||
SM_ASSERT(freePd->allocCount == 0);
|
||||
SM_ASSERT(SM_PAGEDESC_GEN(freePd) == SMGenNum_Free);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
pd2Addr = &pd2->next;
|
||||
}
|
||||
}
|
||||
SM_ASSERT(0);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define SM_MAX_SMALL_OBJECTS_PER_PAGE (1 << (sizeof(SMSmallObjCount) * 8))
|
||||
#define SM_MAX_PAGE_COUNT (((PRUword)-1) / SM_PAGE_SIZE)
|
||||
|
||||
void*
|
||||
sm_PoolAllocLargeObj(SMPool* pool, SMGenNum genNum, PRUword size)
|
||||
{
|
||||
SMPageCount nPages, i;
|
||||
SMPage* obj;
|
||||
|
||||
SM_ASSERT(size > SM_MAX_SMALLOBJECT_SIZE);
|
||||
|
||||
nPages = SM_PAGE_COUNT(size);
|
||||
/* depending on compile-time values, this whole test might go away */
|
||||
if (SM_MAX_PAGE_COUNT > SM_MAX_SMALL_OBJECTS_PER_PAGE) {
|
||||
if (nPages > SM_MAX_SMALL_OBJECTS_PER_PAGE) {
|
||||
/* must fit in allocCount field -- for 32 bits, this allows
|
||||
objects up to 256M in size (4096 * 2^16) */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
obj = sm_NewCluster(&sm_PageMgr, nPages);
|
||||
if (obj) {
|
||||
SMPageDesc* pd = SM_OBJECT_PAGEDESC(obj);
|
||||
SMObjDesc* objTable = &pd->largeObjDesc;
|
||||
SMPageDesc* pd1 = pd + 1;
|
||||
for (i = 1; i < nPages; i++) {
|
||||
SM_PAGEDESC_INIT(pd1, SM_LARGE_OBJECT_BUCKET, genNum, objTable, 0);
|
||||
pd1->next = pd; /* the next field points all pages back to the head */
|
||||
pd1->allocCount = 0; /* subsequent pages have allocCount == 0 */
|
||||
pd1++;
|
||||
}
|
||||
SM_PAGEDESC_INIT(pd, SM_LARGE_OBJECT_BUCKET, genNum, objTable, 0);
|
||||
pd->allocCount = (SMSmallObjCount)nPages;
|
||||
SM_OBJDESC_SET_ALLOCATED(objTable);
|
||||
pd->next = pool->sweepList[SM_LARGE_OBJECT_BUCKET].sweepPage;
|
||||
pool->sweepList[SM_LARGE_OBJECT_BUCKET].sweepPage = pd;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SMBucket sm_AntiBucket[SM_ALLOC_BUCKETS - 1];
|
||||
SMPool* sm_MallocPool = NULL;
|
||||
|
||||
void
|
||||
sm_InitAntiBuckets(void)
|
||||
{
|
||||
SMBucket i;
|
||||
|
||||
SM_ASSERT(SM_PAGEDESC_BUCKET_COUNT <= SM_MAX_VALUE(SMBucket));
|
||||
SM_ASSERT(SM_ALLOC_BUCKETS <= SM_PAGEDESC_BUCKET_COUNT);
|
||||
|
||||
for (i = 0; i < SM_ALLOC_BUCKETS - 1; i++) {
|
||||
SMSmallObjSize size = sm_ObjectSize[i];
|
||||
SMSmallObjSize antiSize = SM_PAGE_SIZE / size;
|
||||
SMBucket antiBucket;
|
||||
if (i >= SM_FIRST_MIDSIZE_BUCKET)
|
||||
antiSize++; /* add one for non-integral number of objects per page */
|
||||
SM_GET_ALLOC_BUCKET(antiBucket, antiSize);
|
||||
sm_AntiBucket[i] = antiBucket;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static PRBool mallocInitialized = PR_FALSE;
|
||||
static SMPool sm_StaticMallocPool;
|
||||
|
||||
SM_IMPLEMENT(PRStatus)
|
||||
SM_InitMalloc(PRUword initialHeapSize, PRUword maxHeapSize)
|
||||
{
|
||||
PRStatus status;
|
||||
|
||||
if (sm_MallocPool)
|
||||
return PR_SUCCESS;
|
||||
sm_MallocPool = &sm_StaticMallocPool;
|
||||
|
||||
/* Make sure we don't need more size buckets than we allotted for. */
|
||||
SM_ASSERT(SM_ALLOC_BUCKETS <= SM_PAGEDESC_BUCKET_COUNT);
|
||||
|
||||
sm_InitFastDivision();
|
||||
sm_InitAntiBuckets();
|
||||
|
||||
status = sm_InitPageMgr(&sm_PageMgr,
|
||||
SM_PAGE_COUNT(initialHeapSize),
|
||||
SM_PAGE_COUNT(maxHeapSize));
|
||||
if (status != PR_SUCCESS) return status;
|
||||
|
||||
return sm_InitPool(sm_MallocPool);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRStatus)
|
||||
SM_CleanupMalloc(void)
|
||||
{
|
||||
sm_FiniPool(sm_MallocPool);
|
||||
sm_FiniPageMgr(&sm_PageMgr);
|
||||
sm_MallocPool = NULL;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* We add a pair of pages simultaneously to malloc space
|
||||
so that they can hold each other's object descriptors. */
|
||||
static void*
|
||||
sm_AddMallocPagePair(SMBucket bucket)
|
||||
{
|
||||
void* obj = NULL;
|
||||
SMSmallObjCount allocIndex = 0;
|
||||
SMPage* page = SM_NEW_PAGE(&sm_PageMgr);
|
||||
if (page) {
|
||||
SMBucket antiBucket = sm_AntiBucket[bucket];
|
||||
SMObjDesc* objTable = (SMObjDesc*)
|
||||
sm_PoolAllocObj(sm_MallocPool, antiBucket);
|
||||
if (objTable == NULL) {
|
||||
/* allocate an anti-page too */
|
||||
SMObjDesc* antiObjTable;
|
||||
SMPage* antiPage = SM_NEW_PAGE(&sm_PageMgr);
|
||||
if (antiPage == NULL) {
|
||||
/* can't get the anti-page */
|
||||
SM_DESTROY_PAGE(&sm_PageMgr, page);
|
||||
return NULL;
|
||||
}
|
||||
antiObjTable = (SMObjDesc*)page;
|
||||
objTable = (SMObjDesc*)
|
||||
sm_PoolInitPage(sm_MallocPool, SMGenNum_Malloc, antiBucket,
|
||||
antiPage, antiObjTable, allocIndex++);
|
||||
}
|
||||
obj = sm_PoolInitPage(sm_MallocPool, SMGenNum_Malloc, bucket,
|
||||
page, objTable, allocIndex);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Memory Pool variation of Malloc and Friends
|
||||
******************************************************************************/
|
||||
|
||||
SM_IMPLEMENT(SMPool*)
|
||||
SM_NewPool(void)
|
||||
{
|
||||
SMPool* pool;
|
||||
SM_ASSERT(sm_MallocPool);
|
||||
pool = (SMPool*)SM_Malloc(sizeof(SMPool));
|
||||
if (pool == NULL) return NULL;
|
||||
sm_InitPool(pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DeletePool(SMPool* pool)
|
||||
{
|
||||
if (pool) {
|
||||
sm_FiniPool(pool);
|
||||
SM_Free(pool);
|
||||
}
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_PoolMalloc(SMPool* pool, PRUword size)
|
||||
{
|
||||
void* result;
|
||||
SMBucket bucket;
|
||||
PRMonitor* mon;
|
||||
PRUword allocSize;
|
||||
SM_GET_ALLOC_BUCKET(bucket, size);
|
||||
|
||||
SM_ASSERT(pool);
|
||||
mon = pool->sweepList[bucket].monitor;
|
||||
if (!sm_PageMgr.alreadyLocked) /* XXX is this right? */
|
||||
PR_EnterMonitor(mon);
|
||||
|
||||
if (bucket == SM_LARGE_OBJECT_BUCKET) {
|
||||
result = sm_PoolAllocLargeObj(pool, SMGenNum_Malloc, size);
|
||||
/* XXX do a gc if we fail? */
|
||||
allocSize = SM_PAGE_COUNT(size) * SM_PAGE_SIZE;
|
||||
}
|
||||
else {
|
||||
do {
|
||||
result = sm_PoolAllocObj(pool, bucket);
|
||||
allocSize = sm_ObjectSize[bucket];
|
||||
if (result == NULL) {
|
||||
result = sm_AddMallocPagePair(bucket);
|
||||
}
|
||||
} while (result == NULL &&
|
||||
(sm_CompactHook ? sm_CompactHook(allocSize) : PR_FALSE));
|
||||
/* XXX need to account for allocAmount due to page pair */
|
||||
}
|
||||
|
||||
if (result) {
|
||||
SM_POOL_SET_ALLOC_STATS(pool, allocSize, size);
|
||||
pool->allocAmount += allocSize;
|
||||
DBG_MEMSET(result, SM_MALLOC_PATTERN, size);
|
||||
DBG_MEMSET((char*)result + size, SM_UNUSED_PATTERN,
|
||||
allocSize - size);
|
||||
}
|
||||
|
||||
if (!sm_PageMgr.alreadyLocked) /* XXX is this right? */
|
||||
PR_ExitMonitor(mon);
|
||||
return result;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_PoolFree(SMPool* pool, void* ptr)
|
||||
{
|
||||
SMPageCount pageNum = SM_PAGE_NUMBER(ptr);
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTable[pageNum];
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
SMSweepList* sweepList;
|
||||
PRMonitor* mon;
|
||||
PRUword allocSize;
|
||||
|
||||
SM_ASSERT(pool);
|
||||
SM_ASSERT(ptr);
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
|
||||
sweepList = &pool->sweepList[bucket];
|
||||
mon = sweepList->monitor;
|
||||
DBG_MEMSET(ptr, SM_FREE_PATTERN, sm_ObjectSize[bucket]);
|
||||
|
||||
if (!sm_PageMgr.alreadyLocked) /* XXX is this right? */
|
||||
PR_EnterMonitor(mon);
|
||||
|
||||
if (bucket == SM_LARGE_OBJECT_BUCKET) {
|
||||
SMPageDesc** pdAddr = &sweepList->sweepPage;
|
||||
SMPageDesc* pd2;
|
||||
while ((pd2 = *pdAddr) != NULL) {
|
||||
if (pd2 == pd) {
|
||||
*pdAddr = pd2->next;
|
||||
goto found;
|
||||
}
|
||||
pdAddr = &pd2->next;
|
||||
}
|
||||
SM_ASSERT(0);
|
||||
found:
|
||||
SM_POOL_FREE_LARGE_OBJ(pool, ptr);
|
||||
allocSize = pd->allocCount * SM_PAGE_SIZE;
|
||||
}
|
||||
else {
|
||||
SM_POOL_FREE_OBJ(pool, ptr);
|
||||
if (pd->allocCount == 1) {
|
||||
SMPageCount objTabPageNum = SM_PAGE_NUMBER(pd->objTable);
|
||||
SMPageDesc* objTabPd = &sm_PageMgr.pageTable[objTabPageNum];
|
||||
if (objTabPd->allocCount == 1
|
||||
&& SM_PAGE_NUMBER(objTabPd->objTable) == pageNum) {
|
||||
/* Then we found a malloc page pair -- free them both,
|
||||
but first break the malloc pair cycle */
|
||||
void* ot1 = pd->objTable;
|
||||
void* ot2 = objTabPd->objTable;
|
||||
|
||||
pd->objTable = NULL;
|
||||
objTabPd->objTable = NULL;
|
||||
sm_PoolRemovePage(pool, bucket, pd);
|
||||
sm_PoolRemovePage(pool, sm_AntiBucket[bucket], objTabPd);
|
||||
/* XXX need to account for allocAmount due to page pair */
|
||||
}
|
||||
}
|
||||
allocSize = sm_ObjectSize[bucket];
|
||||
}
|
||||
|
||||
pool->allocAmount -= allocSize;
|
||||
|
||||
if (!sm_PageMgr.alreadyLocked) /* XXX is this right? */
|
||||
PR_ExitMonitor(mon);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_PoolCalloc(SMPool* pool, PRUword size, PRUword count)
|
||||
{
|
||||
PRUword fullSize = size * count;
|
||||
void* result = SM_PoolMalloc(pool, fullSize);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
memset(result, 0, fullSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_PoolRealloc(SMPool* pool, void* ptr, PRUword size)
|
||||
{
|
||||
PRUword curSize;
|
||||
PRWord diff;
|
||||
void* result;
|
||||
|
||||
SM_OBJECT_GROSS_SIZE(&curSize, ptr);
|
||||
result = SM_PoolMalloc(pool, size);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
memcpy(result, ptr, curSize);
|
||||
diff = size - curSize;
|
||||
if (diff > 0)
|
||||
memset((char*)result + curSize, 0, diff);
|
||||
SM_PoolFree(pool, ptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_PoolStrdup(SMPool* pool, void* str)
|
||||
{
|
||||
PRUword curSize;
|
||||
SM_OBJECT_GROSS_SIZE(&curSize, str);
|
||||
return SM_PoolStrndup(pool, str, curSize);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_PoolStrndup(SMPool* pool, void* str, PRUword size)
|
||||
{
|
||||
void* result = SM_PoolMalloc(pool, size);
|
||||
if (result == NULL) return NULL;
|
||||
memcpy(result, str, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Malloc and Friends
|
||||
******************************************************************************/
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_Malloc(PRUword size)
|
||||
{
|
||||
return SM_PoolMalloc(sm_MallocPool, size);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_Free(void* ptr)
|
||||
{
|
||||
SM_PoolFree(sm_MallocPool, ptr);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_Calloc(PRUword size, PRUword count)
|
||||
{
|
||||
return SM_PoolCalloc(sm_MallocPool, size, count);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_Realloc(void* ptr, PRUword size)
|
||||
{
|
||||
return SM_PoolRealloc(sm_MallocPool, ptr, size);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_Strdup(void* str)
|
||||
{
|
||||
return SM_PoolStrdup(sm_MallocPool, str);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void*)
|
||||
SM_Strndup(void* str, PRUword size)
|
||||
{
|
||||
return SM_PoolStrndup(sm_MallocPool, str, size);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include "smheap.h"
|
||||
|
||||
void
|
||||
sm_PoolVerifyPage(SMPool* pool, SMBucket bucket, SMPageDesc* pd)
|
||||
{
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMSweepList* sweepList = &pool->sweepList[bucket];
|
||||
SMPageDesc* pd2 = sweepList->sweepPage;
|
||||
while (pd2 != NULL) {
|
||||
if (pd2 == pd) {
|
||||
int j, cnt = 0;
|
||||
SMObjDesc* objTable = pd->objTable;
|
||||
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
|
||||
SMPool* pool2 = (genNum == SMGenNum_Malloc)
|
||||
? sm_MallocPool
|
||||
: &sm_Heap.gen[genNum].pool;
|
||||
SM_ASSERT(pool == pool2);
|
||||
SM_ASSERT(SM_PAGEDESC_BUCKET(pd) == bucket);
|
||||
for (j = 0; j < objsPerPage; j++) {
|
||||
if (SM_OBJDESC_IS_ALLOCATED(&objTable[j]))
|
||||
cnt++;
|
||||
}
|
||||
SM_ASSERT(0 < cnt && cnt < objsPerPage);
|
||||
SM_ASSERT(cnt == pd->allocCount);
|
||||
return;
|
||||
}
|
||||
pd2 = pd2->next;
|
||||
}
|
||||
SM_ASSERT(pd->allocCount == 0 || pd->allocCount == objsPerPage);
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/******************************************************************************/
|
123
ef/gc/src/smstack.c
Normal file
123
ef/gc/src/smstack.c
Normal file
@ -0,0 +1,123 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smstack.h"
|
||||
|
||||
#define SM_SEG_END(seg) \
|
||||
((SMObject**)((char*)(seg) + (SM_STACK_SEG_PAGES * SM_PAGE_SIZE)))
|
||||
|
||||
SMObject*
|
||||
sm_StackUnderflow(SMStack* stack)
|
||||
{
|
||||
SMStackSegment* prev = NULL;
|
||||
SMStackSegment* next;
|
||||
if (stack->segment == NULL)
|
||||
return NULL;
|
||||
prev = stack->segment->header.prev;
|
||||
if (prev == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Introduce a lag in the segments deallocated. That way we don't
|
||||
thrash as we push and pop across segment boundaries. */
|
||||
next = stack->segment->header.next;
|
||||
if (next) {
|
||||
sm_DestroyCluster(&sm_PageMgr, (SMPage*)next, SM_STACK_SEG_PAGES);
|
||||
stack->segment->header.next = NULL;
|
||||
}
|
||||
|
||||
stack->segment = prev;
|
||||
stack->min = &prev->element;
|
||||
stack->max = SM_SEG_END(prev);
|
||||
stack->top = stack->max;
|
||||
return SM_STACK_POP0(stack);
|
||||
}
|
||||
|
||||
PRBool
|
||||
sm_StackOverflow(SMStack* stack, SMObject* obj)
|
||||
{
|
||||
SMStackSegment* next = NULL;
|
||||
if (stack->segment) {
|
||||
next = stack->segment->header.next;
|
||||
}
|
||||
if (next == NULL) {
|
||||
next = (SMStackSegment*)sm_NewCluster(&sm_PageMgr, SM_STACK_SEG_PAGES);
|
||||
if (next == NULL) {
|
||||
stack->overflowCount++;
|
||||
return PR_FALSE;
|
||||
}
|
||||
next->header.prev = stack->segment;
|
||||
next->header.next = NULL;
|
||||
}
|
||||
stack->segment = next;
|
||||
stack->min = &next->element;
|
||||
stack->top = &next->element;
|
||||
stack->max = SM_SEG_END(next);
|
||||
SM_STACK_PUSH0(stack, obj);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRUword
|
||||
sm_StackSize(SMStack* stack)
|
||||
{
|
||||
PRUword size = 0;
|
||||
SMStackSegment* seg = stack->segment;
|
||||
if (seg) {
|
||||
while (seg->header.next != NULL) {
|
||||
seg = seg->header.next;
|
||||
}
|
||||
do {
|
||||
SMStackSegment* prev = seg->header.prev;
|
||||
size += (SM_STACK_SEG_PAGES * SM_PAGE_SIZE);
|
||||
seg = prev;
|
||||
} while (seg);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void
|
||||
sm_DestroyStack(SMStack* stack)
|
||||
{
|
||||
SMStackSegment* seg = stack->segment;
|
||||
if (seg) {
|
||||
while (seg->header.next != NULL) {
|
||||
seg = seg->header.next;
|
||||
}
|
||||
do {
|
||||
SMStackSegment* prev = seg->header.prev;
|
||||
sm_DestroyCluster(&sm_PageMgr, (SMPage*)seg, SM_STACK_SEG_PAGES);
|
||||
seg = prev;
|
||||
} while (seg);
|
||||
}
|
||||
SM_INIT_STACK(stack);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
456
ef/gc/src/smtrav.c
Normal file
456
ef/gc/src/smtrav.c
Normal file
@ -0,0 +1,456 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "smtrav.h"
|
||||
#include "smheap.h"
|
||||
|
||||
SM_IMPLEMENT(PRWord)
|
||||
SM_TraverseObjectFields(SMObject* obj,
|
||||
SMTraverseFieldProc traverseProc, void* traverseClosure)
|
||||
{
|
||||
SMClass* clazz = SM_OBJECT_CLASS(obj);
|
||||
if (clazz) {
|
||||
switch (SM_CLASS_KIND(clazz)) {
|
||||
case SMClassKind_ObjectClass: {
|
||||
SMFieldDesc* fieldDesc = SM_CLASS_GET_INST_FIELD_DESCS(clazz);
|
||||
PRUint16 i, fdCount = SM_CLASS_GET_INST_FIELDS_COUNT(clazz);
|
||||
for (i = 0; i < fdCount; i++) {
|
||||
PRWord offset = fieldDesc->offset;
|
||||
PRWord count = fieldDesc->count;
|
||||
SMObject** base = SM_OBJECT_FIELDS(obj);
|
||||
SMObject** ref = &base[offset];
|
||||
SMObject* field;
|
||||
while (count-- > 0) {
|
||||
field = *ref++;
|
||||
if (field) {
|
||||
PRWord status = traverseProc(field, ref - base, traverseClosure);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMClassKind_ArrayClass: {
|
||||
if (SM_IS_OBJECT_ARRAY_CLASS(clazz)) {
|
||||
SMArray* arr = (SMArray*)SM_ENSURE(SMObject, obj);
|
||||
PRUword size = SM_ARRAY_SIZE(arr);
|
||||
SMObject** elementRefs = SM_ARRAY_ELEMENTS(arr);
|
||||
SMObject** base = elementRefs;
|
||||
SMObject* element;
|
||||
while (size-- > 0) {
|
||||
element = *elementRefs++;
|
||||
if (element) {
|
||||
PRWord status =
|
||||
traverseProc(element,
|
||||
elementRefs - base,
|
||||
traverseClosure);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRWord)
|
||||
SM_TraversePageObjects(SMPage* page,
|
||||
SMTraverseObjectProc traverseProc, void* traverseClosure)
|
||||
{
|
||||
SMPageCount pageNum = SM_PAGE_NUMBER(page);
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTable[pageNum];
|
||||
if (SM_PAGEDESC_GEN(pd) != SMGenNum_Free) {
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMObjDesc* objTable = pd->objTable;
|
||||
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
|
||||
SMSmallObjCount i;
|
||||
SMObjStruct* obj = (SMObjStruct*)SM_PAGE_BASE(page);
|
||||
for (i = 0; i < objsPerPage; i++) {
|
||||
SMObjDesc* od = &objTable[i];
|
||||
if (!SM_OBJDESC_WAS_FREE(od)) {
|
||||
PRWord status = traverseProc(SM_ENCRYPT_OBJECT(obj), i, objSize, traverseClosure);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
obj = (SMObjStruct*)((char*)obj + objSize);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRWord)
|
||||
SM_TraverseGenPages(SMGenNum genNum,
|
||||
SMTraversePageProc traverseProc, void* traverseClosure)
|
||||
{
|
||||
SMPageCount i;
|
||||
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
|
||||
if (SM_PAGEDESC_GEN(pd) == genNum) {
|
||||
SMPage* page = SM_PAGEDESC_PAGE(pd);
|
||||
PRWord status = traverseProc(page, traverseClosure);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct SMTraversePageObjectsClosure {
|
||||
SMTraverseObjectProc traverseObject;
|
||||
void* traverseData;
|
||||
} SMTraversePageObjectsClosure;
|
||||
|
||||
static PRWord
|
||||
sm_TraversePageObjects(SMPage* page, void* traverseClosure)
|
||||
{
|
||||
SMTraversePageObjectsClosure* c = (SMTraversePageObjectsClosure*)traverseClosure;
|
||||
return SM_TraversePageObjects(page, c->traverseObject, c->traverseData);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRWord)
|
||||
SM_TraverseGenObjects(SMGenNum genNum,
|
||||
SMTraverseObjectProc traverseProc, void* traverseClosure)
|
||||
{
|
||||
SMTraversePageObjectsClosure c;
|
||||
c.traverseObject = traverseProc;
|
||||
c.traverseData = traverseClosure;
|
||||
return SM_TraverseGenPages(genNum, sm_TraversePageObjects, &c);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRWord)
|
||||
SM_TraverseAllPages(SMTraversePageProc traverseProc, void* traverseClosure)
|
||||
{
|
||||
SMGenNum genNum;
|
||||
for (genNum = SMGenNum_Freshman; genNum <= SMGenNum_Malloc; genNum++) {
|
||||
PRWord status = SM_TraverseGenPages(genNum, traverseProc, traverseClosure);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(PRWord)
|
||||
SM_TraverseAllObjects(SMTraverseObjectProc traverseProc, void* traverseClosure)
|
||||
{
|
||||
SMTraversePageObjectsClosure c;
|
||||
c.traverseObject = traverseProc;
|
||||
c.traverseData = traverseClosure;
|
||||
return SM_TraverseAllPages(sm_TraversePageObjects, &c);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef SM_DUMP /* define this if you want dump code in the final product */
|
||||
|
||||
static void
|
||||
sm_DumpIndent(FILE* out, PRUword indent)
|
||||
{
|
||||
while (indent--) {
|
||||
fputc(' ', out);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sm_DumpHex(FILE* out, void* addr, PRUword count, PRUword wordsPerLine,
|
||||
PRUword indent)
|
||||
{
|
||||
void** p = (void**)addr;
|
||||
while (count > 0) {
|
||||
PRUword i = wordsPerLine;
|
||||
sm_DumpIndent(out, indent);
|
||||
if (i > count)
|
||||
i = count;
|
||||
count -= i;
|
||||
while (i--) {
|
||||
fprintf(out, "%.8x", *p++);
|
||||
if (i)
|
||||
fputc(' ', out);
|
||||
}
|
||||
fputc('\n', out);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct SMDumpClosure {
|
||||
FILE* out;
|
||||
PRUword flags;
|
||||
} SMDumpClosure;
|
||||
|
||||
static PRWord
|
||||
sm_DumpObj(void* p, SMSmallObjCount index, PRUword objSize, void* closure)
|
||||
{
|
||||
SMObject* obj = (SMObject*)p;
|
||||
SMObjStruct* dobj = SM_DECRYPT_OBJECT(obj);
|
||||
SMClass* clazz = SM_OBJECT_CLASS(obj);
|
||||
SMDumpClosure* c = (SMDumpClosure*)closure;
|
||||
SMPageCount pageNum = SM_PAGE_NUMBER(dobj);
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTable[pageNum];
|
||||
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
|
||||
SMObjDesc* od = SM_OBJECT_HEADER_FROM_PAGEDESC(dobj, pd);
|
||||
SMSmallObjCount pageOffset = (SMSmallObjCount)SM_PAGE_OFFSET(dobj);
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
SMSmallObjCount objNum = SM_DIV(pageOffset, bucket);
|
||||
SM_ASSERT(objNum == index);
|
||||
if (c->flags & SMDumpFlag_GCState && SM_OBJDESC_IS_UNMARKED(od)) {
|
||||
fprintf(c->out, " %3u %08x size=%u UNMARKED\n", objNum, dobj, objSize);
|
||||
}
|
||||
else if (c->flags & SMDumpFlag_GCState && SM_OBJDESC_IS_UNTRACED(od)) {
|
||||
/* should never happen */
|
||||
fprintf(c->out, " %3u %08x size=%u UNTRACED!!!\n", objNum, dobj, objSize);
|
||||
}
|
||||
else if (c->flags & SMDumpFlag_GCState && SM_OBJDESC_IS_FORWARDED(od)) {
|
||||
fprintf(c->out, " %3u %08x size=%u FORWARDED to %08x\n",
|
||||
objNum, dobj, objSize, SM_OBJECT_FORWARDING_ADDR(obj));
|
||||
}
|
||||
else if (SM_OBJDESC_IS_FREE(od)) {
|
||||
return 0;
|
||||
}
|
||||
else if (genNum == SMGenNum_Malloc) {
|
||||
fprintf(c->out, " %3u %08x size=%u\n", objNum, dobj, objSize);
|
||||
}
|
||||
else if (SM_OBJDESC_NEEDS_FINALIZATION(od)) {
|
||||
SM_ASSERT(SM_OBJDESC_IS_FINALIZABLE(od));
|
||||
fprintf(c->out, " %3u %08x class=%08x needs finalization\n",
|
||||
objNum, dobj, SM_OBJECT_CLASS(obj));
|
||||
}
|
||||
else {
|
||||
fprintf(c->out, " %3u %08x class=%08x%s%s%s\n",
|
||||
objNum, dobj, clazz,
|
||||
SM_OBJDESC_IS_FINALIZABLE(od) ? " finalizable" : "",
|
||||
SM_OBJDESC_IS_COPYABLE(od) ? " copyable" : "",
|
||||
SM_OBJDESC_IS_PINNED(od) ? " pinned" : "");
|
||||
}
|
||||
if (c->flags & SMDumpFlag_Detailed) {
|
||||
sm_DumpHex(c->out, dobj, objSize >> SM_REF_BITS, 4, 8);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PRWord
|
||||
sm_DumpPageObjects(SMPage* page, void* closure)
|
||||
{
|
||||
SMDumpClosure* c = (SMDumpClosure*)closure;
|
||||
FILE* out = c->out;
|
||||
PRUword flags = c->flags;
|
||||
SMPageDesc* pd = SM_OBJECT_PAGEDESC(page);
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMPageCount pageCnt = pd - sm_PageMgr.pageTableMem;
|
||||
if (SM_PAGEDESC_GEN(pd) == SMGenNum_Free) {
|
||||
fprintf(out, " Page %u FREE\n", pageCnt);
|
||||
return 0;
|
||||
}
|
||||
if (bucket == SM_LARGE_OBJECT_BUCKET) {
|
||||
if (SM_PAGEDESC_IS_LARGE_OBJECT_START(pd)) {
|
||||
SMDumpClosure c2;
|
||||
if (flags & SMDumpFlag_Abbreviate) {
|
||||
/* turn off detailed dumps for large objects */
|
||||
c2.flags = flags & ~SMDumpFlag_Detailed;
|
||||
c2.out = out;
|
||||
closure = &c2;
|
||||
}
|
||||
fprintf(out, " Page %u gen=%u objSize=%u (large object)",
|
||||
pageCnt, SM_PAGEDESC_GEN(pd), SM_PAGE_WIDTH(pd->allocCount));
|
||||
if (flags & SMDumpFlag_GCState)
|
||||
fprintf(out, " objTable=%08x\n", pd->objTable);
|
||||
else
|
||||
fputc('\n', out);
|
||||
sm_DumpObj(page, 0, SM_PAGE_WIDTH(pd->allocCount), closure);
|
||||
}
|
||||
/* else skip it -- we've already dumped the object */
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
fprintf(out, " Page %u gen=%u objSize=%u allocCount=%u",
|
||||
pageCnt, SM_PAGEDESC_GEN(pd), objSize, pd->allocCount);
|
||||
if (flags & SMDumpFlag_GCState)
|
||||
fprintf(out, " objTable=%08x\n", pd->objTable);
|
||||
else
|
||||
fputc('\n', out);
|
||||
}
|
||||
return SM_TraversePageObjects(page, sm_DumpObj, closure);
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DumpMallocHeap(FILE* out, PRUword flags)
|
||||
{
|
||||
SMDumpClosure closure;
|
||||
closure.out = out;
|
||||
closure.flags = flags;
|
||||
fprintf(out, "--SportModel-Malloc-Heap-Dump------------------------------\n");
|
||||
(void)SM_TraverseGenPages(SMGenNum_Malloc, sm_DumpPageObjects, &closure);
|
||||
fprintf(out, "-----------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DumpGCHeap(FILE* out, PRUword flags)
|
||||
{
|
||||
SMGenNum genNum;
|
||||
SMDumpClosure closure;
|
||||
closure.out = out;
|
||||
closure.flags = flags;
|
||||
fprintf(out, "--SportModel-GC-Heap-Dump----------------------------------\n");
|
||||
for (genNum = SMGenNum_Freshman; genNum <= SMGenNum_Static; genNum++) {
|
||||
fprintf(out, "Gen %u\n", genNum);
|
||||
(void)SM_TraverseGenPages(genNum, sm_DumpPageObjects, &closure);
|
||||
}
|
||||
fprintf(out, "-----------------------------------------------------------\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DumpPage(FILE* out, void* addr, PRUword flags)
|
||||
{
|
||||
SMPage* page = SM_PAGE_BASE(addr);
|
||||
SMDumpClosure c;
|
||||
c.out = out;
|
||||
c.flags = flags;
|
||||
(void)sm_DumpPageObjects(page, &c);
|
||||
#if 0
|
||||
SMPageCount pageNum = SM_PAGE_NUMBER(addr);
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTable[pageNum];
|
||||
SMPageCount pageCnt = pd - sm_PageMgr.pageTableMem;
|
||||
if (SM_PAGEDESC_GEN(pd) == SMGenNum_Free) {
|
||||
fprintf(out, "Page %u FREE\n", pageCnt);
|
||||
}
|
||||
else {
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
|
||||
SMSmallObjSize objSize = sm_ObjectSize[bucket];
|
||||
SMObjDesc* objTable = pd->objTable;
|
||||
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
|
||||
SMSmallObjCount i;
|
||||
SMObjStruct* obj = (SMObjStruct*)SM_PAGE_BASE(addr);
|
||||
SMDumpClosure c;
|
||||
c.out = out;
|
||||
c.flags = flags;
|
||||
fprintf(out, "Page %u objSize=%u gen=%u allocCount=%u\n",
|
||||
pageCnt, objSize, genNum, pd->allocCount);
|
||||
for (i = 0; i < objsPerPage; i++) {
|
||||
SMObjDesc od = objTable[i];
|
||||
(void)sm_DumpObj(SM_ENCRYPT_OBJECT(obj), i, objSize, &c);
|
||||
obj = (SMObjStruct*)((char*)obj + objSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DumpHeap(FILE* out, PRUword flags)
|
||||
{
|
||||
SM_DumpMallocHeap(out, flags);
|
||||
SM_DumpGCHeap(out, flags);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define SM_PRINT_WIDTH 64
|
||||
#define SM_END_OF_PAGE(pos) \
|
||||
(((pos) % SM_PRINT_WIDTH) == (SM_PRINT_WIDTH - 1))
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_DumpPageMap(FILE* out, PRUword flags)
|
||||
{
|
||||
SMPageCount i;
|
||||
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
|
||||
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
|
||||
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
|
||||
int isBlacklisted = SM_PAGEDESC_IS_BLACKLISTED(pd);
|
||||
if (bucket == SM_LARGE_OBJECT_BUCKET) {
|
||||
if (isBlacklisted)
|
||||
fputc('L', out);
|
||||
else
|
||||
fputc('l', out);
|
||||
}
|
||||
else {
|
||||
char c;
|
||||
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
|
||||
if (SM_IS_GC_SPACE(genNum)) {
|
||||
if (isBlacklisted)
|
||||
c = 'G';
|
||||
else
|
||||
c = 'g';
|
||||
}
|
||||
else if (genNum == SMGenNum_Malloc) {
|
||||
if (isBlacklisted)
|
||||
c = 'M';
|
||||
else
|
||||
c = 'm';
|
||||
}
|
||||
else if (genNum == SMGenNum_Free) {
|
||||
if (isBlacklisted)
|
||||
c = 'F';
|
||||
else
|
||||
c = 'f';
|
||||
}
|
||||
else { /* unknown */
|
||||
if (isBlacklisted)
|
||||
c = 'X';
|
||||
else
|
||||
c = 'x';
|
||||
}
|
||||
fprintf(out, "%c", c);
|
||||
}
|
||||
if (SM_END_OF_PAGE(i))
|
||||
fputc('\n', out);
|
||||
}
|
||||
fputc('\n', out);
|
||||
}
|
||||
|
||||
#endif /* SM_DUMP */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
|
||||
FILE* sm_DebugOutput = NULL;
|
||||
|
||||
SM_IMPLEMENT(void)
|
||||
SM_SetTraceOutputFile(FILE* out)
|
||||
{
|
||||
if (out == NULL && sm_DebugOutput) {
|
||||
fflush(sm_DebugOutput);
|
||||
}
|
||||
sm_DebugOutput = out;
|
||||
}
|
||||
|
||||
#endif /* defined(DEBUG) || defined(SM_DUMP) */
|
||||
|
||||
/******************************************************************************/
|
4
ef/gc/test/Makefile
Normal file
4
ef/gc/test/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
DEPTH = ../..
|
||||
|
||||
include manifest.mn
|
||||
include $(DEPTH)/config/rules.mk
|
63
ef/gc/test/alloc.c
Normal file
63
ef/gc/test/alloc.c
Normal file
@ -0,0 +1,63 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Author: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "sm.h"
|
||||
#include "smgen.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
foo(unsigned int num)
|
||||
{
|
||||
unsigned int bin1, bin2;
|
||||
SM_SIZE_BIN(bin1, num);
|
||||
SM_GET_ALLOC_BUCKET(bin2, num);
|
||||
printf("num = %-10u bin1 = %2d bin2 = %2d\n",
|
||||
num, bin1, bin2);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
int i;
|
||||
for (i = 2; i < 32; i++) {
|
||||
unsigned int b = 1 << i;
|
||||
unsigned int p = 1 << (i - 1);
|
||||
|
||||
printf("--- %d\n", i);
|
||||
foo(b - 1);
|
||||
foo(b);
|
||||
foo(b + 1);
|
||||
foo(b + p - 1);
|
||||
foo(b + p);
|
||||
foo(b + p + 1);
|
||||
}
|
||||
}
|
71
ef/gc/test/buckets.c
Normal file
71
ef/gc/test/buckets.c
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "sm.h"
|
||||
#include "smgen.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static void
|
||||
foo(unsigned int size)
|
||||
{
|
||||
PRUword bin, bucket;
|
||||
SM_SIZE_BIN(bin, size);
|
||||
SM_GET_ALLOC_BUCKET(bucket, size);
|
||||
printf("size = %-10u bin = %2d, bucket = %2d\n",
|
||||
size, bin, bucket);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
int i;
|
||||
for (i = SM_MIN_SMALLOBJECT_BITS-1; i <= SM_MAX_SMALLOBJECT_BITS; i++) {
|
||||
unsigned int b = 1 << i;
|
||||
unsigned int p = 1 << (i - 1);
|
||||
|
||||
printf("--- %d\n", i);
|
||||
foo(b - 1);
|
||||
foo(b);
|
||||
if (i < SM_MAX_SMALLOBJECT_BITS) {
|
||||
foo(b + 1);
|
||||
foo(b + p - 1);
|
||||
foo(b + p);
|
||||
foo(b + p + 1);
|
||||
}
|
||||
}
|
||||
printf("page size=%u max=%u min=%u\n",
|
||||
SM_PAGE_SIZE, SM_MAX_SMALLOBJECT_SIZE, SM_MIN_SMALLOBJECT_SIZE);
|
||||
printf("p2buckets=%u midsize=%u firstMid=%u buckets=%u\n",
|
||||
SM_POWER_OF_TWO_BUCKETS, SM_MIDSIZE_BUCKETS,
|
||||
SM_FIRST_MIDSIZE_BUCKET, SM_ALLOC_BUCKETS);
|
||||
}
|
112
ef/gc/test/divtest.c
Normal file
112
ef/gc/test/divtest.c
Normal file
@ -0,0 +1,112 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "sm.h"
|
||||
#include "smgen.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define ITERATIONS (10*1024*1024)
|
||||
#define SIZE 1024
|
||||
int divTable[SIZE];
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
int i, j;
|
||||
clock_t t1, t2, te;
|
||||
int ans1[SIZE], ans2[SIZE];
|
||||
fprintf(stdout, "SportModel: Division algorithm timing analysis test\n");
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0, j = 0; i < ITERATIONS; i++) {
|
||||
ans1[SIZE - 1 - j] = j;
|
||||
if (++j == SIZE) j = 0;
|
||||
}
|
||||
t2 = clock();
|
||||
te = t2 - t1;
|
||||
|
||||
fprintf(stdout, "empty loop = %ldms\n", te * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
i = ITERATIONS;
|
||||
SM_UNROLLED_WHILE(i, {
|
||||
ans1[SIZE - 1 - j] = j;
|
||||
if (++j == SIZE) j = 0;
|
||||
});
|
||||
t2 = clock();
|
||||
|
||||
fprintf(stdout, "unrolled loop = %ldms\n", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0, j = 0; i < ITERATIONS; i++) {
|
||||
ans1[SIZE - 1 - j] = j / 12;
|
||||
if (++j == SIZE) j = 0;
|
||||
}
|
||||
t2 = clock();
|
||||
|
||||
fprintf(stdout, "div loop = %ldms\n", (t2 - t1 - te) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
/* initialize divTable */
|
||||
for (j = 0; j < SIZE; j++) {
|
||||
divTable[j] = j / 12;
|
||||
}
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0, j = 0; i < ITERATIONS; i++) {
|
||||
ans1[SIZE - 1 - j] = divTable[j];
|
||||
if (++j == SIZE) j = 0;
|
||||
}
|
||||
t2 = clock();
|
||||
|
||||
fprintf(stdout, "lookup loop = %ldms\n", (t2 - t1 - te) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0, j = 0; i < ITERATIONS; i++) {
|
||||
ans2[SIZE - 1 - j] = ((j + 1) * 21845) >> 18;
|
||||
if (++j == SIZE) j = 0;
|
||||
}
|
||||
t2 = clock();
|
||||
|
||||
fprintf(stdout, "rad loop = %ldms\n", (t2 - t1 - te) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
#if 0
|
||||
for (i = 0; i < SIZE; i++) {
|
||||
fprintf(stdout, "%8d %8d %8d (%8d) %8d %8d\n",
|
||||
i, ans1[i], ans2[i], (ans1[i] - ans2[i]), divTable[i],
|
||||
SM_DIV(i, SM_FIRST_MIDSIZE_BUCKET));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
457
ef/gc/test/hashtable.cpp
Normal file
457
ef/gc/test/hashtable.cpp
Normal file
@ -0,0 +1,457 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Author: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "plhash.h"
|
||||
#include "dynahash.h"
|
||||
#include "openhash.h"
|
||||
#include "prlog.h"
|
||||
#include "FastHashTable.h" // Electrical Fire s
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define ITERATIONS 10000
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
//#define HASH_ALLOC 1
|
||||
|
||||
static PLHashNumber
|
||||
HashFunction(const void *key)
|
||||
{
|
||||
return (PLHashNumber)key;
|
||||
}
|
||||
|
||||
static PRIntn
|
||||
HashKeyComparator(const void *v1, const void *v2)
|
||||
{
|
||||
return (int)v1 == (int)v2;
|
||||
}
|
||||
|
||||
static PRIntn
|
||||
HashValueComparator(const void *v1, const void *v2)
|
||||
{
|
||||
#ifdef HASH_ALLOC
|
||||
return *(PRUword*)v1 == *(PRUword*)v2;
|
||||
#else
|
||||
return (PRUword)v1 == (PRUword)v2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
HashTableAdd(PLHashTable* ht, PRUword i)
|
||||
{
|
||||
void* elem;
|
||||
void* value;
|
||||
#ifdef HASH_ALLOC
|
||||
value = malloc(sizeof(PRUword));
|
||||
*(PRUword*)value = i;
|
||||
#else
|
||||
value = (void*)i;
|
||||
#endif
|
||||
elem = PL_HashTableAdd(ht, (const void*)i, value);
|
||||
PR_ASSERT(elem != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
HashTableLookup(PLHashTable* ht, PRUword i)
|
||||
{
|
||||
void* value;
|
||||
PRUword v;
|
||||
value = PL_HashTableLookup(ht, (const void*)i);
|
||||
#ifdef HASH_ALLOC
|
||||
v = *(PRUword*)value;
|
||||
#else
|
||||
v = (PRUword)value;
|
||||
#endif
|
||||
PR_ASSERT(v == i);
|
||||
}
|
||||
|
||||
static void
|
||||
HashTableDelete(PLHashTable* ht, PRUword i)
|
||||
{
|
||||
PRBool wasFound;
|
||||
#ifdef HASH_ALLOC
|
||||
PLHashEntry* entry = *PL_HashTableRawLookup(ht, i, (const void*)i);
|
||||
free(entry->value);
|
||||
#endif
|
||||
wasFound = PL_HashTableRemove(ht, (const void*)i);
|
||||
PR_ASSERT(wasFound);
|
||||
}
|
||||
|
||||
static void
|
||||
TestHashTable(int initialSize)
|
||||
{
|
||||
PRUword i;
|
||||
PRIntervalTime t1, t2;
|
||||
PLHashTable* ht;
|
||||
|
||||
ht = PL_NewHashTable(initialSize, HashFunction, HashKeyComparator,
|
||||
HashValueComparator, NULL, NULL);
|
||||
PR_ASSERT(ht);
|
||||
fprintf(stdout, "prhash\t%d", initialSize);
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
HashTableAdd(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
HashTableLookup(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
HashTableDelete(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus\n", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
PL_HashTableDestroy(ht);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
typedef struct MyHashElement {
|
||||
PLDynahashElement hashElement; /* must be first */
|
||||
PRUint32 key;
|
||||
PRUint32 value;
|
||||
} MyHashElement;
|
||||
|
||||
static PRUint32
|
||||
Dynahash_HashKey(void* context, MyHashElement* x)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
return x->key;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
Dynahash_Equals(void* context, MyHashElement* x, MyHashElement* y)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
return (PRBool)(x->key == y->key);
|
||||
}
|
||||
|
||||
static void
|
||||
Dynahash_Destroy(void* context, MyHashElement* x)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
free(x);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
DynahashAdd(PLDynahash* ht, PRUword i)
|
||||
{
|
||||
PRStatus status;
|
||||
MyHashElement* oldElem;
|
||||
MyHashElement* elem;
|
||||
elem = (MyHashElement*)malloc(sizeof(MyHashElement));
|
||||
PR_ASSERT(elem != NULL);
|
||||
elem->hashElement.next = NULL;
|
||||
elem->key = i;
|
||||
elem->value = i;
|
||||
status = PL_DynahashAdd(ht, (PLDynahashElement*)elem, PR_FALSE,
|
||||
(PLDynahashElement**)&oldElem);
|
||||
PR_ASSERT(status == PR_SUCCESS);
|
||||
free(oldElem);
|
||||
}
|
||||
|
||||
static void
|
||||
DynahashLookup(PLDynahash* ht, PRUword i)
|
||||
{
|
||||
MyHashElement elem;
|
||||
MyHashElement* found;
|
||||
PRBool wasFound;
|
||||
elem.hashElement.next = NULL;
|
||||
elem.key = i;
|
||||
found = (MyHashElement*)PL_DynahashLookup(ht, (PLDynahashElement*)&elem);
|
||||
PR_ASSERT(found);
|
||||
wasFound = (PRBool)(found->value == i);
|
||||
PR_ASSERT(wasFound);
|
||||
}
|
||||
|
||||
static void
|
||||
DynahashDelete(PLDynahash* ht, PRUword i)
|
||||
{
|
||||
MyHashElement elem;
|
||||
MyHashElement* found;
|
||||
PRBool wasFound = PR_FALSE;
|
||||
elem.hashElement.next = NULL;
|
||||
elem.key = i;
|
||||
found = (MyHashElement*)PL_DynahashRemove(ht, (PLDynahashElement*)&elem);
|
||||
PR_ASSERT(found);
|
||||
wasFound = (PRBool)(found->value == i);
|
||||
PR_ASSERT(wasFound);
|
||||
free(found);
|
||||
}
|
||||
|
||||
static void
|
||||
TestDynahash(int initialSize)
|
||||
{
|
||||
PRUword i;
|
||||
PRIntervalTime t1, t2;
|
||||
PLDynahash* ht;
|
||||
|
||||
ht = PL_NewDynahash(initialSize,
|
||||
(PLHashFun)Dynahash_HashKey,
|
||||
(PLEqualFun)Dynahash_Equals,
|
||||
(PLDestroyFun)Dynahash_Destroy,
|
||||
NULL);
|
||||
PR_ASSERT(ht);
|
||||
fprintf(stdout, "dyna\t%d", initialSize);
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
DynahashAdd(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
DynahashLookup(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
DynahashDelete(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus\n", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
PL_DynahashDestroy(ht);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
Openhash_Destroy(void* context, PLOpenhashElement* elem)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
#ifdef HASH_ALLOC
|
||||
free(elem->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
OpenhashAdd(PLOpenhash* ht, PRUword i)
|
||||
{
|
||||
#ifdef HASH_ALLOC
|
||||
void* elem = malloc(sizeof(PRUword));
|
||||
PR_ASSERT(elem != NULL);
|
||||
*(PRUword*)elem = i;
|
||||
#else
|
||||
void* elem = (void*)i;
|
||||
#endif
|
||||
PL_OpenhashAdd(ht, i, elem);
|
||||
}
|
||||
|
||||
static void
|
||||
OpenhashLookup(PLOpenhash* ht, PRUword i)
|
||||
{
|
||||
void* elem = PL_OpenhashLookup(ht, i);
|
||||
#ifdef HASH_ALLOC
|
||||
PR_ASSERT(elem);
|
||||
PR_ASSERT(*(PRUword*)elem == i);
|
||||
#else
|
||||
PR_ASSERT((PRUword)elem == i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
TestOpenhash(int initialSize)
|
||||
{
|
||||
PRUword i;
|
||||
PRIntervalTime t1, t2;
|
||||
PLOpenhash* ht;
|
||||
|
||||
ht = PL_NewOpenhash(initialSize,
|
||||
(PLOpenhashHashFun)NULL,
|
||||
(PLOpenhashEqualFun)NULL,
|
||||
(PLOpenhashDestroyFun)Openhash_Destroy,
|
||||
NULL);
|
||||
PR_ASSERT(ht);
|
||||
fprintf(stdout, "open\t%d", initialSize);
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
OpenhashAdd(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
OpenhashLookup(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus\n", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
PL_OpenhashDestroy(ht);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
class EFHash : public FastHashTable<void*> {
|
||||
public:
|
||||
EFHash(Pool& pool) : FastHashTable<void*>(pool) {}
|
||||
};
|
||||
|
||||
static void
|
||||
EFHashAdd(EFHash* ht, PRUword i)
|
||||
{
|
||||
#ifdef HASH_ALLOC
|
||||
void* elem = malloc(sizeof(PRUword));
|
||||
PR_ASSERT(elem != NULL);
|
||||
*(PRUword*)elem = i;
|
||||
#else
|
||||
void* elem = (void*)i;
|
||||
#endif
|
||||
ht->add((const char*)i, elem);
|
||||
}
|
||||
|
||||
static void
|
||||
EFHashLookup(EFHash* ht, PRUword i)
|
||||
{
|
||||
void* elem;
|
||||
bool found = ht->get((const char*)i, &elem);
|
||||
PR_ASSERT(found);
|
||||
#ifdef HASH_ALLOC
|
||||
PR_ASSERT(*(PRUword*)elem == i);
|
||||
#else
|
||||
PR_ASSERT((PRUword)elem == i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
EFHashDelete(EFHash* ht, PRUword i)
|
||||
{
|
||||
#ifdef HASH_ALLOC
|
||||
void* elem;
|
||||
bool found = ht->get((const char*)i, &elem);
|
||||
free(elem);
|
||||
#endif
|
||||
ht->remove((const char*)i);
|
||||
}
|
||||
|
||||
static void
|
||||
TestEFHash()
|
||||
{
|
||||
PRUword i;
|
||||
PRIntervalTime t1, t2;
|
||||
EFHash* ht;
|
||||
|
||||
Pool* pool = new Pool();
|
||||
PR_ASSERT(pool);
|
||||
ht = new EFHash(*pool);
|
||||
|
||||
PR_ASSERT(ht);
|
||||
fprintf(stdout, "efhash\t");
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
EFHashAdd(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
EFHashLookup(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
EFHashDelete(ht, i);
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(stdout, "\t%ldus\n", PR_IntervalToMicroseconds(t2 - t1));
|
||||
|
||||
delete ht;
|
||||
delete pool;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
#if 0
|
||||
PRBool success = PL_DynahashTest();
|
||||
fprintf(stdout, "DynahashTest => %s\n", success ? "true" : "false");
|
||||
#endif
|
||||
|
||||
fprintf(stdout, "SportModel: Hash table comparison tests\n");
|
||||
|
||||
fprintf(stdout, "\tsize\tadd\tlookup\tremove\n");
|
||||
|
||||
TestHashTable(1024 * 8 - 8);
|
||||
TestHashTable(1024 - 8);
|
||||
TestHashTable(8);
|
||||
|
||||
TestDynahash(1024 * 8 - 8);
|
||||
TestDynahash(1024 - 8);
|
||||
TestDynahash(8);
|
||||
|
||||
TestOpenhash(1024 * 256);
|
||||
TestOpenhash(1024 * 128);
|
||||
TestOpenhash(1024 * 16);
|
||||
|
||||
TestEFHash();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
372
ef/gc/test/hashtest.c
Normal file
372
ef/gc/test/hashtest.c
Normal file
@ -0,0 +1,372 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Author: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "plhash.h"
|
||||
#include "dynahash.h"
|
||||
#include "openhash.h"
|
||||
#include "prlog.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define ITERATIONS 10000
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define HASH_ALLOC 1
|
||||
|
||||
static PLHashNumber
|
||||
HashFunction(const void *key)
|
||||
{
|
||||
return (PLHashNumber)key;
|
||||
}
|
||||
|
||||
static PRIntn
|
||||
HashKeyComparator(const void *v1, const void *v2)
|
||||
{
|
||||
return (int)v1 == (int)v2;
|
||||
}
|
||||
|
||||
static PRIntn
|
||||
HashValueComparator(const void *v1, const void *v2)
|
||||
{
|
||||
#ifdef HASH_ALLOC
|
||||
return *(PRUword*)v1 == *(PRUword*)v2;
|
||||
#else
|
||||
return (PRUword)v1 == (PRUword)v2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
HashTableAdd(PLHashTable* ht, PRUword i)
|
||||
{
|
||||
void* elem;
|
||||
void* value;
|
||||
#ifdef HASH_ALLOC
|
||||
value = malloc(sizeof(PRUword));
|
||||
*(PRUword*)value = i;
|
||||
#else
|
||||
value = (void*)i;
|
||||
#endif
|
||||
elem = PL_HashTableAdd(ht, (const void*)i, value);
|
||||
PR_ASSERT(elem != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
HashTableLookup(PLHashTable* ht, PRUword i)
|
||||
{
|
||||
void* value;
|
||||
PRUword v;
|
||||
value = PL_HashTableLookup(ht, (const void*)i);
|
||||
#ifdef HASH_ALLOC
|
||||
v = *(PRUword*)value;
|
||||
#else
|
||||
v = (PRUword)value;
|
||||
#endif
|
||||
PR_ASSERT(v == i);
|
||||
}
|
||||
|
||||
static void
|
||||
HashTableDelete(PLHashTable* ht, PRUword i)
|
||||
{
|
||||
PRBool wasFound;
|
||||
#ifdef HASH_ALLOC
|
||||
PLHashEntry* entry = *PL_HashTableRawLookup(ht, i, (const void*)i);
|
||||
free(entry->value);
|
||||
#endif
|
||||
wasFound = PL_HashTableRemove(ht, (const void*)i);
|
||||
PR_ASSERT(wasFound);
|
||||
}
|
||||
|
||||
static void
|
||||
TestHashTable(int initialSize)
|
||||
{
|
||||
PRUword i;
|
||||
clock_t t1, t2;
|
||||
PLHashTable* ht;
|
||||
|
||||
ht = PL_NewHashTable(initialSize, HashFunction, HashKeyComparator,
|
||||
HashValueComparator, NULL, NULL);
|
||||
PR_ASSERT(ht);
|
||||
fprintf(stdout, "prhash\t%d", initialSize);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
HashTableAdd(ht, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
HashTableLookup(ht, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
HashTableDelete(ht, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms\n", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
PL_HashTableDestroy(ht);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
typedef struct MyHashElement {
|
||||
PLDynahashElement hashElement; /* must be first */
|
||||
PRUint32 key;
|
||||
PRUint32 value;
|
||||
} MyHashElement;
|
||||
|
||||
static PRUint32
|
||||
Dynahash_HashKey(void* context, MyHashElement* x)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
return x->key;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
Dynahash_Equals(void* context, MyHashElement* x, MyHashElement* y)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
return (PRBool)(x->key == y->key);
|
||||
}
|
||||
|
||||
static void
|
||||
Dynahash_Destroy(void* context, MyHashElement* x)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
free(x);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
DynahashAdd(PLDynahash* dh, PRUword i)
|
||||
{
|
||||
PRStatus status;
|
||||
MyHashElement* oldElem;
|
||||
MyHashElement* elem;
|
||||
elem = (MyHashElement*)malloc(sizeof(MyHashElement));
|
||||
PR_ASSERT(elem != NULL);
|
||||
elem->hashElement.next = NULL;
|
||||
elem->key = i;
|
||||
elem->value = i;
|
||||
status = PL_DynahashAdd(dh, (PLDynahashElement*)elem, PR_FALSE,
|
||||
(PLDynahashElement**)&oldElem);
|
||||
PR_ASSERT(status == PR_SUCCESS);
|
||||
free(oldElem);
|
||||
}
|
||||
|
||||
static void
|
||||
DynahashLookup(PLDynahash* dh, PRUword i)
|
||||
{
|
||||
MyHashElement elem;
|
||||
MyHashElement* found;
|
||||
PRBool wasFound;
|
||||
elem.hashElement.next = NULL;
|
||||
elem.key = i;
|
||||
found = (MyHashElement*)PL_DynahashLookup(dh, (PLDynahashElement*)&elem);
|
||||
PR_ASSERT(found);
|
||||
wasFound = (PRBool)(found->value == i);
|
||||
PR_ASSERT(wasFound);
|
||||
}
|
||||
|
||||
static void
|
||||
DynahashDelete(PLDynahash* dh, PRUword i)
|
||||
{
|
||||
MyHashElement elem;
|
||||
MyHashElement* found;
|
||||
PRBool wasFound = PR_FALSE;
|
||||
elem.hashElement.next = NULL;
|
||||
elem.key = i;
|
||||
found = (MyHashElement*)PL_DynahashRemove(dh, (PLDynahashElement*)&elem);
|
||||
PR_ASSERT(found);
|
||||
wasFound = (PRBool)(found->value == i);
|
||||
PR_ASSERT(wasFound);
|
||||
free(found);
|
||||
}
|
||||
|
||||
static void
|
||||
TestDynahash(int initialSize)
|
||||
{
|
||||
PRUword i;
|
||||
clock_t t1, t2;
|
||||
PLDynahash* dh;
|
||||
|
||||
dh = PL_NewDynahash(initialSize,
|
||||
(PLHashFun)Dynahash_HashKey,
|
||||
(PLEqualFun)Dynahash_Equals,
|
||||
(PLDestroyFun)Dynahash_Destroy,
|
||||
NULL);
|
||||
PR_ASSERT(dh);
|
||||
fprintf(stdout, "dyna\t%d", initialSize);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
DynahashAdd(dh, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
DynahashLookup(dh, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
DynahashDelete(dh, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms\n", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
PL_DynahashDestroy(dh);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
Openhash_Destroy(void* context, PLOpenhashElement* elem)
|
||||
{
|
||||
#ifdef XP_MAC
|
||||
#pragma unused(context)
|
||||
#endif
|
||||
#ifdef HASH_ALLOC
|
||||
free(elem->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
OpenhashAdd(PLOpenhash* dh, PRUword i)
|
||||
{
|
||||
#ifdef HASH_ALLOC
|
||||
void* elem = malloc(sizeof(PRUword));
|
||||
PR_ASSERT(elem != NULL);
|
||||
*(PRUword*)elem = i;
|
||||
#else
|
||||
void* elem = (void*)i;
|
||||
#endif
|
||||
PL_OpenhashAdd(dh, i, elem);
|
||||
}
|
||||
|
||||
static void
|
||||
OpenhashLookup(PLOpenhash* dh, PRUword i)
|
||||
{
|
||||
void* elem = PL_OpenhashLookup(dh, i);
|
||||
#ifdef HASH_ALLOC
|
||||
PR_ASSERT(elem);
|
||||
PR_ASSERT(*(PRUword*)elem == i);
|
||||
#else
|
||||
PR_ASSERT((PRUword)elem == i);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
TestOpenhash(int initialSize)
|
||||
{
|
||||
PRUword i;
|
||||
clock_t t1, t2;
|
||||
PLOpenhash* dh;
|
||||
|
||||
dh = PL_NewOpenhash(initialSize,
|
||||
(PLOpenhashHashFun)NULL,
|
||||
(PLOpenhashEqualFun)NULL,
|
||||
(PLOpenhashDestroyFun)Openhash_Destroy,
|
||||
NULL);
|
||||
PR_ASSERT(dh);
|
||||
fprintf(stdout, "open\t%d", initialSize);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
OpenhashAdd(dh, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
t1 = clock();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
OpenhashLookup(dh, i);
|
||||
}
|
||||
t2 = clock();
|
||||
fprintf(stdout, "\t%ldms\n", (t2 - t1) * 1000 / CLOCKS_PER_SEC);
|
||||
|
||||
PL_OpenhashDestroy(dh);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
#if 0
|
||||
PRBool success = PL_DynahashTest();
|
||||
fprintf(stdout, "DynahashTest => %s\n", success ? "true" : "false");
|
||||
#endif
|
||||
|
||||
fprintf(stdout, "SportModel: Hash table comparison tests\n");
|
||||
|
||||
fprintf(stdout, "\tsize\tadd\tlookup\tremove\n");
|
||||
|
||||
TestHashTable(1024 * 8 - 8);
|
||||
TestHashTable(1024 - 8);
|
||||
TestHashTable(8);
|
||||
|
||||
TestDynahash(1024 * 8 - 8);
|
||||
TestDynahash(1024 - 8);
|
||||
TestDynahash(8);
|
||||
|
||||
TestOpenhash(1024 * 1024);
|
||||
TestOpenhash(1024 * 256);
|
||||
TestOpenhash(1024 * 128);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
34
ef/gc/test/makefile.win
Normal file
34
ef/gc/test/makefile.win
Normal file
@ -0,0 +1,34 @@
|
||||
DEPTH = ..\..
|
||||
|
||||
TARGETS = hashtable.exe hashtest.exe smtest.exe msgctest.exe maltest.exe divtest.exe
|
||||
|
||||
include <$(DEPTH)/config/rules.mak>
|
||||
|
||||
LIBS = ef.lib \
|
||||
$(DEPTH)\dist\WIN32_D.OBJ\lib\sm3240.lib \
|
||||
$(DEPTH)\dist\WIN32_D.OBJ\lib\libnspr21.lib \
|
||||
$(DEPTH)\dist\WIN32_D.OBJ\lib\libplds21.lib \
|
||||
$(DEPTH)\dist\WIN32_D.OBJ\lib\libmsgc21.lib \
|
||||
|
||||
CFLAGS = $(CFLAGS) -DSM_DUMP -I$(PUBLIC)\sm -I$(PUBLIC)\nspr20 $(OS_LIBS) $(LIBS)
|
||||
CPPFLAGS = $(CFLAGS) -DSM_DUMP -I$(PUBLIC)\sm -I$(PUBLIC)\nspr20 $(OS_LIBS) -I..\eflib\ $(LIBS)
|
||||
|
||||
!ifndef MOZ_PROF # assume we use a write barrier when profiling
|
||||
CFLAGS = $(CFLAGS) -DSM_NO_WRITE_BARRIER -DSM_CHECK_PTRS -DSM_DEBUG_HEADER
|
||||
CPPFLAGS = $(CPPFLAGS) -DSM_NO_WRITE_BARRIER -DSM_CHECK_PTRS -DSM_DEBUG_HEADER
|
||||
!endif
|
||||
|
||||
import:
|
||||
echo cp $(DIST)\bin\sm3240.dll $(DIST)\bin\libnspr21.dll $(DIST)\bin\libplds21.dll $(DIST)\bin\libmsgc21.dll ..\eflib\win32_o.obj\ef.lib .
|
||||
cp $(DIST)\bin\sm3240.dll $(DIST)\bin\libnspr21.dll $(DIST)\bin\libplds21.dll $(DIST)\bin\libmsgc21.dll ..\eflib\win32_o.obj\ef.lib .
|
||||
# cp $(DIST)\bin\sm3240.dll .
|
||||
|
||||
install:: import $(TARGETS)
|
||||
|
||||
clobber::
|
||||
$(RM) *.mnw *.exe *.ilk *.obj *.pdb
|
||||
|
||||
#ifdef HAVE_PURIFY
|
||||
javah.pure: $(OBJS) $(LIBRARIES)
|
||||
$(PURECCF) -o $@ $(OBJS) $(LIBRARIES)
|
||||
#endif
|
175
ef/gc/test/maltest.c
Normal file
175
ef/gc/test/maltest.c
Normal file
@ -0,0 +1,175 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Recovered by: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "prinit.h"
|
||||
#include "sm.h"
|
||||
#include "smgen.h"
|
||||
#include "smheap.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
extern void* malloc(size_t);
|
||||
extern void free(void*);
|
||||
|
||||
#define K 1024
|
||||
#define M (K*K)
|
||||
#define PAGE (4*K)
|
||||
#define MIN_HEAP_SIZE (K*PAGE)
|
||||
#define MAX_HEAP_SIZE (16*M)
|
||||
|
||||
#define ITERATIONS (16*K)
|
||||
#define KEEP_AMOUNT K
|
||||
|
||||
FILE* out;
|
||||
int toFile = 1;
|
||||
int verbose = 0;
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
PRStatus status;
|
||||
PRIntervalTime t1, t2;
|
||||
void* foo;
|
||||
void* bar;
|
||||
int i, ms, keepIndex = 0;
|
||||
struct tm *newtime;
|
||||
time_t aclock;
|
||||
void* keep[KEEP_AMOUNT];
|
||||
|
||||
if (toFile) {
|
||||
out = fopen("maltest.out", "w+");
|
||||
if (out == NULL) {
|
||||
perror("Can't open maltest.out");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
out = stdout;
|
||||
|
||||
time(&aclock);
|
||||
newtime = localtime(&aclock);
|
||||
fprintf(out, "SportModel Malloc Test Program: %s\n", asctime(newtime));
|
||||
// fprintf(out, "SM_ALLOC_BUCKETS=%u\n", SM_ALLOC_BUCKETS);
|
||||
|
||||
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
|
||||
status = SM_InitGC(MIN_HEAP_SIZE, MAX_HEAP_SIZE,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (status != PR_SUCCESS)
|
||||
return -1;
|
||||
|
||||
foo = SM_Malloc(32);
|
||||
SM_Free(foo);
|
||||
foo = SM_Malloc(33);
|
||||
SM_Free(foo);
|
||||
|
||||
foo = SM_Malloc(32);
|
||||
bar = SM_Malloc(32);
|
||||
SM_Free(foo);
|
||||
SM_Free(bar);
|
||||
|
||||
/* try to malloc some large objects */
|
||||
foo = SM_Malloc(8 * K);
|
||||
bar = SM_Malloc(9 * K);
|
||||
SM_Free(foo);
|
||||
SM_Free(bar);
|
||||
|
||||
foo = SM_Calloc(sizeof(char), 4 * K);
|
||||
memset(foo, 'a', 4*K - 1);
|
||||
bar = SM_Strdup(foo);
|
||||
if (strcmp(foo, bar) != 0)
|
||||
fprintf(out, "strdup failed!\n");
|
||||
if (verbose) {
|
||||
for (i = 0; i < 4*K-1; i++) {
|
||||
fprintf(out, "%c", ((char*)bar)[i]);
|
||||
if (i % 32 == 31)
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
SM_Free(foo);
|
||||
SM_Free(bar);
|
||||
|
||||
for (i = 0; i < KEEP_AMOUNT; i++)
|
||||
keep[i] = NULL;
|
||||
|
||||
/* time the platform malloc/free */
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
int size = i % SM_MAX_SMALLOBJECT_SIZE;
|
||||
int j = i % KEEP_AMOUNT;
|
||||
if (keep[j])
|
||||
free(keep[j]);
|
||||
keep[j] = malloc(size);
|
||||
}
|
||||
for (i = 0; i < KEEP_AMOUNT; i++) {
|
||||
if (keep[i]) {
|
||||
free(keep[i]);
|
||||
keep[i] = NULL;
|
||||
}
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
ms = PR_IntervalToMilliseconds(t2 - t1);
|
||||
fprintf(out, "time for %uk malloc/free operations: %ums\n", ITERATIONS / K, ms);
|
||||
|
||||
/* time the SportModel malloc/free */
|
||||
t1 = PR_IntervalNow();
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
int size = i % SM_MAX_SMALLOBJECT_SIZE;
|
||||
int j = i % KEEP_AMOUNT;
|
||||
if (keep[j])
|
||||
SM_Free(keep[j]);
|
||||
keep[j] = SM_Malloc(size);
|
||||
}
|
||||
for (i = 0; i < KEEP_AMOUNT; i++) {
|
||||
if (keep[i]) {
|
||||
SM_Free(keep[i]);
|
||||
keep[i] = NULL;
|
||||
}
|
||||
}
|
||||
t2 = PR_IntervalNow();
|
||||
ms = PR_IntervalToMilliseconds(t2 - t1);
|
||||
fprintf(out, "time for %uk SM_Malloc/SM_Free operations: %ums\n", ITERATIONS / K, ms);
|
||||
|
||||
#ifdef DEBUG
|
||||
SM_DumpStats(out, PR_TRUE);
|
||||
#endif
|
||||
|
||||
fflush(out);
|
||||
|
||||
SM_CleanupGC(PR_FALSE);
|
||||
status = PR_Cleanup();
|
||||
if (status != PR_SUCCESS)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
10
ef/gc/test/manifest.mn
Normal file
10
ef/gc/test/manifest.mn
Normal file
@ -0,0 +1,10 @@
|
||||
MODULE = sm
|
||||
|
||||
DEPTH = ../..
|
||||
|
||||
CSRCS = \
|
||||
maltest.c \
|
||||
$(NULL)
|
||||
|
||||
REQUIRES = sm nspr20
|
||||
|
656
ef/gc/test/msgctest.c
Normal file
656
ef/gc/test/msgctest.c
Normal file
@ -0,0 +1,656 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Author: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "prinit.h"
|
||||
#ifdef XP_MAC
|
||||
#include "pprthred.h"
|
||||
#else
|
||||
#include "private/pprthred.h"
|
||||
#endif
|
||||
#include "prgc.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define K 1024
|
||||
#define M (K*K)
|
||||
#define PAGE (4*K)
|
||||
#define MIN_HEAP_SIZE (128*PAGE)
|
||||
#define MAX_HEAP_SIZE (16*M)
|
||||
|
||||
FILE* out;
|
||||
int toFile = 1;
|
||||
int verbose = 1;
|
||||
int testLargeObjects = 1;
|
||||
|
||||
GCInfo *gcInfo = 0;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
typedef void (PR_CALLBACK *FinalizeFun)(void* obj);
|
||||
|
||||
typedef struct fieldblock {
|
||||
int type;
|
||||
int access;
|
||||
int offset;
|
||||
} fieldblock;
|
||||
|
||||
typedef enum AType {
|
||||
T_NORMAL_OBJECT, /* must be 0 */
|
||||
T_CLASS, /* aka, array of object */
|
||||
T_INT,
|
||||
T_LONG,
|
||||
T_DOUBLE
|
||||
} AType;
|
||||
|
||||
#define fieldIsArray(fb) ((fb)->type == T_CLASS)
|
||||
#define fieldIsClass(fb) ((fb)->type == T_NORMAL_OBJECT)
|
||||
|
||||
typedef struct ClassClass {
|
||||
PRUword instanceSize;
|
||||
int fieldsCount;
|
||||
fieldblock* fields;
|
||||
struct methodtable* methodTable;
|
||||
FinalizeFun finalize;
|
||||
} ClassClass;
|
||||
|
||||
#define cbSuperclass(cb) NULL
|
||||
#define cbFields(cb) ((cb)->fields)
|
||||
#define cbFieldsCount(cb) ((cb)->fieldsCount)
|
||||
#define ACC_STATIC 1
|
||||
#define cbFinalizer(cb) ((cb)->finalize)
|
||||
#define cbInstanceSize(cb) ((cb)->instanceSize)
|
||||
#define cbMethodTable(cb) ((cb)->methodTable)
|
||||
|
||||
#define class_offset(o) obj_length(o)
|
||||
|
||||
struct methodblock;
|
||||
|
||||
struct methodtable {
|
||||
ClassClass* classdescriptor;
|
||||
/* methodtables must be at 32-byte aligned -- adding 7 will get around this */
|
||||
struct methodblock* methods[7];
|
||||
};
|
||||
|
||||
#define ALIGN_MT(mt) ((struct methodtable*)((((PRUword)(mt)) + FLAG_MASK) & ~FLAG_MASK))
|
||||
|
||||
#define obj_methodtable(obj) ((obj)->methods)
|
||||
#define obj_classblock(obj) ((obj)->methods->classdescriptor)
|
||||
|
||||
#define METHOD_FLAG_BITS 5L
|
||||
#define FLAG_MASK ((1L<<METHOD_FLAG_BITS)-1L) /* valid flag bits */
|
||||
#define METHOD_MASK (~FLAG_MASK) /* valid mtable ptr bits */
|
||||
#define LENGTH_MASK METHOD_MASK
|
||||
#define rawtype(o) (((unsigned long) (o)->methods) & FLAG_MASK)
|
||||
#define obj_flags(o) rawtype(o)
|
||||
#define obj_length(o) \
|
||||
(((unsigned long) (o)->methods) >> METHOD_FLAG_BITS)
|
||||
|
||||
#define tt2(m) ((m) & FLAG_MASK)
|
||||
#define atype(m) tt2(m)
|
||||
#define mkatype(t,l) ((struct methodtable *) (((l) << METHOD_FLAG_BITS)|(t)))
|
||||
|
||||
typedef struct ClassObject ClassObject;
|
||||
|
||||
typedef struct JHandle {
|
||||
struct methodtable* methods;
|
||||
ClassObject* obj;
|
||||
} JHandle;
|
||||
|
||||
typedef JHandle HObject;
|
||||
#define OBJECT HObject*
|
||||
#define unhand(h) ((h)->obj)
|
||||
|
||||
typedef struct HArrayOfObject {
|
||||
JHandle super;
|
||||
PRUword length;
|
||||
HObject* body[1];
|
||||
} HArrayOfObject;
|
||||
|
||||
static void PR_CALLBACK
|
||||
ScanJavaHandle(void GCPTR *gch)
|
||||
{
|
||||
JHandle *h = (JHandle *) gch;
|
||||
ClassClass *cb;
|
||||
void **sub;
|
||||
ClassObject *p;
|
||||
void (*livePointer)(void *base);
|
||||
void (*liveBlock)(void **base, PRInt32 count);
|
||||
int32 n;
|
||||
|
||||
liveBlock = gcInfo->liveBlock;
|
||||
livePointer = gcInfo->livePointer;
|
||||
|
||||
(*livePointer)((void*) h->obj);
|
||||
p = unhand(h);
|
||||
|
||||
switch (obj_flags(h)) {
|
||||
case T_NORMAL_OBJECT:
|
||||
if (obj_methodtable(h)) {
|
||||
cb = obj_classblock(h);
|
||||
do {
|
||||
struct fieldblock *fb = cbFields(cb);
|
||||
|
||||
/* Scan the fields of the class */
|
||||
n = cbFieldsCount(cb);
|
||||
while (--n >= 0) {
|
||||
if ((fieldIsArray(fb) || fieldIsClass(fb))
|
||||
&& !(fb->access & ACC_STATIC)) {
|
||||
sub = (void **) ((char *) p + fb->offset);
|
||||
(*livePointer)(*sub);
|
||||
}
|
||||
fb++;
|
||||
}
|
||||
if (cbSuperclass(cb) == 0) {
|
||||
break;
|
||||
}
|
||||
cb = cbSuperclass(cb);
|
||||
} while (cb);
|
||||
} else {
|
||||
/*
|
||||
** If the object doesn't yet have it's method table, we can't
|
||||
** scan it. This means the GC examined the handle before the
|
||||
** allocation code (realObjAlloc/AllocHandle) has initialized
|
||||
** it.
|
||||
*/
|
||||
}
|
||||
break;
|
||||
|
||||
case T_CLASS: /* an array of objects */
|
||||
/*
|
||||
** Have the GC scan all of the elements and the ClassClass*
|
||||
** stored at the end.
|
||||
*/
|
||||
n = class_offset(h) + 1;
|
||||
(*liveBlock)((void**)((HArrayOfObject *) h)->body, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
DumpJavaHandle(FILE *out, void GCPTR *gch, PRBool detailed, int indent)
|
||||
{
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
SummarizeJavaHandle(void GCPTR *obj, size_t bytes)
|
||||
{
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
FinalJavaHandle(void GCPTR *gch)
|
||||
{
|
||||
JHandle *handle = (JHandle GCPTR *) gch;
|
||||
ClassClass* cb = obj_classblock(handle);
|
||||
cbFinalizer(cb)(handle);
|
||||
}
|
||||
|
||||
GCType unscannedType = {
|
||||
0,
|
||||
0,
|
||||
DumpJavaHandle,
|
||||
SummarizeJavaHandle,
|
||||
0,
|
||||
0,
|
||||
'U',
|
||||
0
|
||||
};
|
||||
int unscannedTIX;
|
||||
|
||||
GCType scannedType = {
|
||||
ScanJavaHandle,
|
||||
0,
|
||||
DumpJavaHandle,
|
||||
SummarizeJavaHandle,
|
||||
0,
|
||||
0,
|
||||
'S',
|
||||
NULL
|
||||
};
|
||||
int scannedTIX;
|
||||
|
||||
GCType scannedFinalType = {
|
||||
ScanJavaHandle,
|
||||
FinalJavaHandle,
|
||||
DumpJavaHandle,
|
||||
SummarizeJavaHandle,
|
||||
0,
|
||||
0,
|
||||
'F',
|
||||
NULL
|
||||
};
|
||||
int scannedFinalTIX;
|
||||
|
||||
static void*
|
||||
realObjAlloc(struct methodtable *mptr, long bytes, PRBool inIsClass)
|
||||
{
|
||||
ClassClass *cb;
|
||||
HObject *h;
|
||||
int tix, flags;
|
||||
|
||||
/* See if the object requires finalization or scanning */
|
||||
if (!inIsClass) {
|
||||
switch (atype((unsigned long) mptr)) {
|
||||
case T_NORMAL_OBJECT:
|
||||
/* Normal object that may require finalization or double alignment. */
|
||||
/* XXX pessimistic assumption for now */
|
||||
flags = PR_ALLOC_DOUBLE | PR_ALLOC_CLEAN;
|
||||
cb = mptr->classdescriptor;
|
||||
if (cbFinalizer(cb)) {
|
||||
tix = scannedFinalTIX;
|
||||
#if 0
|
||||
} else if (cbFlags(cb) & CCF_IsWeakLink) {
|
||||
tix = weakLinkTIX;
|
||||
#endif
|
||||
} else {
|
||||
tix = scannedTIX;
|
||||
}
|
||||
break;
|
||||
case T_CLASS: /* An array of objects */
|
||||
flags = PR_ALLOC_CLEAN;
|
||||
tix = scannedTIX;
|
||||
break;
|
||||
|
||||
case T_LONG: /* An array of java long's */
|
||||
case T_DOUBLE: /* An array of double's */
|
||||
flags = PR_ALLOC_DOUBLE | PR_ALLOC_CLEAN;
|
||||
tix = unscannedTIX;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* An array of something (char, byte, ...) */
|
||||
flags = PR_ALLOC_CLEAN;
|
||||
tix = unscannedTIX;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
flags = PR_ALLOC_CLEAN;
|
||||
tix = unscannedTIX; // classClassTIX;
|
||||
}
|
||||
|
||||
if (flags & PR_ALLOC_DOUBLE) {
|
||||
/* Double align bytes */
|
||||
bytes = (bytes + BYTES_PER_DWORD - 1) >> BYTES_PER_DWORD_LOG2;
|
||||
bytes <<= BYTES_PER_DWORD_LOG2;
|
||||
} else {
|
||||
/* Word align bytes */
|
||||
bytes = (bytes + BYTES_PER_WORD - 1) >> BYTES_PER_WORD_LOG2;
|
||||
bytes <<= BYTES_PER_WORD_LOG2;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add in space for the handle (which is two words so we won't mess
|
||||
** up the double alignment)
|
||||
*/
|
||||
/*
|
||||
Achtung Explorer!! Proceed carefully!!!
|
||||
|
||||
On certain machines a JHandle MUST be double aligned.
|
||||
|
||||
*/
|
||||
bytes += sizeof(JHandle);
|
||||
|
||||
/* Allocate object and handle memory */
|
||||
h = (JHandle*) PR_AllocMemory(bytes, tix, flags);
|
||||
if (!h) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Fill in handle.
|
||||
**
|
||||
** Note: if the gc runs before these two stores happen, it's ok
|
||||
** because it will find the reference to the new object on the C
|
||||
** stack. The reference to the class object will be found in the
|
||||
** calling frames C stack (see ObjAlloc below).
|
||||
*/
|
||||
h->methods = mptr;
|
||||
|
||||
h->obj = (ClassObject*) (h + 1);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#define T_ELEMENT_SIZE(t) \
|
||||
(((t) == T_DOUBLE) ? sizeof(double) : sizeof(void*))
|
||||
|
||||
#define MAX_INT 0x7fffL
|
||||
|
||||
static PRInt32
|
||||
sizearray(PRInt32 t, PRInt32 l)
|
||||
{
|
||||
PRInt32 size = 0;
|
||||
|
||||
switch(t){
|
||||
case T_CLASS:
|
||||
size = sizeof(OBJECT);
|
||||
break;
|
||||
default:
|
||||
size = T_ELEMENT_SIZE(t);
|
||||
break;
|
||||
}
|
||||
/* Check for overflow of PRInt32 size */
|
||||
if ( l && (size > MAX_INT/l )) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size *= l;
|
||||
return size;
|
||||
}
|
||||
|
||||
static HObject*
|
||||
AllocArray(PRInt32 t, PRInt32 l)
|
||||
{
|
||||
HObject *handle;
|
||||
|
||||
PRInt32 bytes;
|
||||
|
||||
bytes = sizearray(t, l);
|
||||
|
||||
if (bytes < 0) {
|
||||
/* This is probably an overflow error - t*l > MAX_INT */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes += (t == T_CLASS ? sizeof(OBJECT) : 0);
|
||||
handle = realObjAlloc((struct methodtable *) mkatype(t, l), bytes, FALSE);
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
static HObject*
|
||||
AllocObject(ClassClass *cb, long n0)
|
||||
{
|
||||
HObject *handle;
|
||||
|
||||
n0 = cbInstanceSize(cb);
|
||||
handle = realObjAlloc(cbMethodTable(cb), n0, FALSE);
|
||||
return handle;
|
||||
}
|
||||
|
||||
PR_EXTERN(void)
|
||||
PR_PrintGCStats(void);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
ClassClass* MyClass;
|
||||
ClassClass* MyLargeClass;
|
||||
|
||||
typedef struct MyClassStruct {
|
||||
HObject super;
|
||||
struct MyClassStruct* next;
|
||||
int value;
|
||||
// void* foo[2]; /* to see internal fragmentation */
|
||||
} MyClassStruct;
|
||||
|
||||
static void
|
||||
MyClass_finalize(HObject* self)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(out, "finalizing %d\n", ((MyClassStruct*)self)->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
InitClasses(void)
|
||||
{
|
||||
MyClass = (ClassClass*)malloc(sizeof(ClassClass));
|
||||
MyClass->instanceSize = sizeof(MyClassStruct);
|
||||
MyClass->fieldsCount = 2;
|
||||
MyClass->fields = (fieldblock*)calloc(sizeof(fieldblock), 2);
|
||||
MyClass->fields[0].type = T_NORMAL_OBJECT;
|
||||
MyClass->fields[0].access = 0;
|
||||
MyClass->fields[0].offset = offsetof(MyClassStruct, next) - sizeof(HObject);
|
||||
MyClass->fields[1].type = T_INT;
|
||||
MyClass->fields[1].access = 0;
|
||||
MyClass->fields[1].offset = offsetof(MyClassStruct, value) - sizeof(HObject);
|
||||
MyClass->methodTable = ALIGN_MT(malloc(sizeof(struct methodtable)));
|
||||
MyClass->methodTable->classdescriptor = MyClass;
|
||||
MyClass->finalize = MyClass_finalize;
|
||||
|
||||
MyLargeClass = (ClassClass*)malloc(sizeof(ClassClass));
|
||||
MyLargeClass->instanceSize = 4097;
|
||||
MyLargeClass->fieldsCount = 2;
|
||||
MyLargeClass->fields = (fieldblock*)calloc(sizeof(fieldblock), 2);
|
||||
MyLargeClass->fields[0].type = T_NORMAL_OBJECT;
|
||||
MyLargeClass->fields[0].access = 0;
|
||||
MyLargeClass->fields[0].offset = offsetof(MyClassStruct, next) - sizeof(HObject);
|
||||
MyLargeClass->fields[1].type = T_INT;
|
||||
MyLargeClass->fields[1].access = 0;
|
||||
MyLargeClass->fields[1].offset = offsetof(MyClassStruct, value) - sizeof(HObject);
|
||||
MyLargeClass->methodTable = ALIGN_MT(malloc(sizeof(struct methodtable)));
|
||||
MyLargeClass->methodTable->classdescriptor = MyLargeClass;
|
||||
MyLargeClass->finalize = MyClass_finalize;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
MyClassStruct* root = NULL;
|
||||
|
||||
static void PR_CALLBACK
|
||||
MarkRoots(void* data)
|
||||
{
|
||||
void (*livePointer)(void *base);
|
||||
livePointer = gcInfo->livePointer;
|
||||
gcInfo->livePointer(root);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
BeforeHook(SMGenNum genNum, PRUword collectCount,
|
||||
PRBool copyCycle, void* closure)
|
||||
{
|
||||
#ifdef xDEBUG
|
||||
FILE* out = (FILE*)closure;
|
||||
if (verbose) {
|
||||
fprintf(out, "Before collection %u gen %u %s\n", collectCount, genNum,
|
||||
(copyCycle ? "(copy cycle)" : ""));
|
||||
PR_DumpGCHeap(out, dumpFlags);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
AfterHook(SMGenNum genNum, PRUword collectCount,
|
||||
PRBool copyCycle, void* closure)
|
||||
{
|
||||
#ifdef xDEBUG
|
||||
FILE* out = (FILE*)closure;
|
||||
if (verbose) {
|
||||
fprintf(out, "After collection %u gen %u %s\n", collectCount, genNum,
|
||||
(copyCycle ? "(copy cycle)" : ""));
|
||||
PR_DumpGCHeap(out, dumpFlags);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ITERATIONS 10000
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
PRStatus status;
|
||||
HObject* arr;
|
||||
MyClassStruct* obj;
|
||||
MyClassStruct* conserv;
|
||||
int i, cnt = 0;
|
||||
struct tm *newtime;
|
||||
time_t aclock;
|
||||
void* randomPtr;
|
||||
PRIntervalTime t1, t2;
|
||||
|
||||
if (toFile) {
|
||||
out = fopen("msgctest.out", "w+");
|
||||
if (out == NULL) {
|
||||
perror("Can't open msgctest.out");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
out = stdout;
|
||||
|
||||
time(&aclock);
|
||||
newtime = localtime(&aclock);
|
||||
fprintf(out, "SportModel Garbage Collector Test Program: %s\n", asctime(newtime));
|
||||
|
||||
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
|
||||
PR_SetThreadGCAble();
|
||||
PR_InitGC(0, MIN_HEAP_SIZE, 4*K, 0);
|
||||
gcInfo = PR_GetGCInfo();
|
||||
PR_RegisterRootFinder(MarkRoots, "mark msgctest roots", 0);
|
||||
scannedTIX = PR_RegisterType(&scannedType);
|
||||
scannedFinalTIX = PR_RegisterType(&scannedFinalType);
|
||||
|
||||
InitClasses();
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
|
||||
arr = AllocArray(T_CLASS, 13); /* gets collected */
|
||||
|
||||
randomPtr = (char*)arr + (4096 * 200);
|
||||
|
||||
arr = AllocArray(T_CLASS, 13); /* try an array */
|
||||
((HArrayOfObject*)arr)->body[3] = (HObject*)root;
|
||||
root = (MyClassStruct*)arr;
|
||||
arr = NULL;
|
||||
|
||||
PR_GC();
|
||||
|
||||
if (testLargeObjects) {
|
||||
/* test large object allocation */
|
||||
obj = (MyClassStruct*)AllocObject(MyLargeClass, 0); /* gets collected */
|
||||
obj->value = cnt++;
|
||||
|
||||
obj = (MyClassStruct*)AllocObject(MyLargeClass, 0);
|
||||
obj->value = cnt++;
|
||||
obj->next = root;
|
||||
root = obj;
|
||||
|
||||
PR_GC();
|
||||
}
|
||||
|
||||
obj = (MyClassStruct*)AllocObject(MyClass, 0); /* gets collected */
|
||||
obj->value = cnt++;
|
||||
obj = (MyClassStruct*)AllocObject(MyClass, 0);
|
||||
obj->value = cnt++;
|
||||
obj->next = root; /* link this into the root list */
|
||||
root = obj;
|
||||
conserv = (MyClassStruct*)AllocObject(MyClass, 0); /* keep a conservative root */
|
||||
conserv->value = cnt++;
|
||||
|
||||
arr = AllocArray(T_CLASS, 13); /* try an array */
|
||||
((HArrayOfObject*)arr)->body[3] = (HObject*)root;
|
||||
root = (MyClassStruct*)arr;
|
||||
arr = NULL;
|
||||
|
||||
obj = NULL;
|
||||
PR_GC();
|
||||
PR_ForceFinalize();
|
||||
|
||||
/* Force collection to occur for every subsequent object,
|
||||
for testing purposes. */
|
||||
#if 0
|
||||
SM_SetCollectThresholds(sizeof(MyClassStruct),
|
||||
sizeof(MyClassStruct),
|
||||
sizeof(MyClassStruct),
|
||||
sizeof(MyClassStruct),
|
||||
0);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
int size = rand() / 100;
|
||||
/* allocate some random garbage */
|
||||
// fprintf(out, "allocating array of size %d\n", size);
|
||||
AllocArray(T_CLASS, size);
|
||||
|
||||
obj = (MyClassStruct*)AllocObject(MyClass, 0); /* gets collected */
|
||||
obj->value = cnt++;
|
||||
obj->next = root;
|
||||
root = obj;
|
||||
|
||||
//SM_DumpStats(out, dumpFlags);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (i = 0; i < 10; i++) {
|
||||
PR_GC();
|
||||
}
|
||||
// PR_PrintGCStats();
|
||||
#endif
|
||||
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(out, "Test completed in %ums\n", PR_IntervalToMilliseconds(t2 - t1));
|
||||
|
||||
if (verbose) {
|
||||
/* list out all the objects */
|
||||
i = 0;
|
||||
obj = root;
|
||||
while (obj != NULL) {
|
||||
MyClassStruct* next;
|
||||
if (obj_flags((HObject*)obj) == T_NORMAL_OBJECT) {
|
||||
fprintf(out, "%4d ", obj->value);
|
||||
next = obj->next;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "[arr %p] ", obj);
|
||||
next = (MyClassStruct*)((HArrayOfObject*)obj)->body[3];
|
||||
}
|
||||
if (i++ % 20 == 19)
|
||||
fprintf(out, "\n");
|
||||
obj = next;
|
||||
}
|
||||
fprintf(out, "\nconserv = %d\n", conserv->value);
|
||||
}
|
||||
|
||||
randomPtr = NULL;
|
||||
root = NULL;
|
||||
obj = NULL;
|
||||
conserv = NULL;
|
||||
PR_GC();
|
||||
PR_ForceFinalize();
|
||||
PR_GC();
|
||||
PR_ForceFinalize();
|
||||
|
||||
#ifdef DEBUG
|
||||
// PR_DumpGCHeap(out, 0);
|
||||
#endif
|
||||
|
||||
PR_ShutdownGC(PR_TRUE);
|
||||
status = PR_Cleanup();
|
||||
if (status != PR_SUCCESS)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
121
ef/gc/test/pagetest.c
Normal file
121
ef/gc/test/pagetest.c
Normal file
@ -0,0 +1,121 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Author: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "sm.h"
|
||||
#include "smpage.h"
|
||||
#include "prinrval.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PG_COUNT 1024
|
||||
SMPage* pg[PG_COUNT];
|
||||
int pgIndex = 0;
|
||||
int allocCount = 0;
|
||||
int allocSize = 0;
|
||||
int failed = 0;
|
||||
int failedSize = 0;
|
||||
|
||||
#define MAX_CLUSTER_SIZE 128
|
||||
#define ITERATIONS (10*1024)
|
||||
#define ALLOC_WEIGHT 8
|
||||
|
||||
#ifdef xDEBUG
|
||||
#define FPRINTF(args) fprintf args
|
||||
#else
|
||||
#define FPRINTF(args)
|
||||
#endif
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int i;
|
||||
SMPageMgr* pm;
|
||||
PRIntervalTime t1, t2;
|
||||
int time;
|
||||
|
||||
fprintf(stdout, "SportModel Garbage Collector: Page Test\n");
|
||||
|
||||
pm = (SMPageMgr*)malloc(sizeof(SMPageMgr));
|
||||
SM_InitPageMgr(pm, 1, PG_COUNT * MAX_CLUSTER_SIZE / 2);
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
int alloc = rand() & ((1 << ALLOC_WEIGHT) - 1);
|
||||
if ((!alloc && allocCount > 0) || allocCount >= PG_COUNT) { /* then do a deallocation */
|
||||
/* find a page to deallocate */
|
||||
int p = rand() * PG_COUNT / RAND_MAX;
|
||||
while (pg[p] == NULL) {
|
||||
p++;
|
||||
if (p >= PG_COUNT)
|
||||
p = 0;
|
||||
}
|
||||
allocCount--;
|
||||
allocSize -= *(int*)pg[p];
|
||||
FPRINTF((stdout, "%5d Dealloc size %4d index %4d count %5d total %10d addr %8p\n",
|
||||
i, *(int*)pg[p], p, allocCount, allocSize, pg[p]));
|
||||
SM_DestroyCluster(pm, pg[p], *(int*)(pg[p]));
|
||||
pg[p] = NULL;
|
||||
}
|
||||
else { /* else do an allocation */
|
||||
PRWord x = rand() * MAX_CLUSTER_SIZE / RAND_MAX + 1;
|
||||
while (pg[pgIndex] != NULL) {
|
||||
pgIndex++;
|
||||
if (pgIndex >= PG_COUNT)
|
||||
pgIndex = 0;
|
||||
}
|
||||
pg[pgIndex] = SM_NewCluster(pm, x);
|
||||
if (pg[pgIndex]) {
|
||||
*(int*)(pg[pgIndex]) = x;
|
||||
allocCount++;
|
||||
allocSize += x;
|
||||
}
|
||||
else {
|
||||
failed++;
|
||||
failedSize += x;
|
||||
}
|
||||
FPRINTF((stdout, "%5d Alloc size %4d index %4d count %5d total %10d addr %8p\n",
|
||||
i, x, pgIndex, allocCount, allocSize, pg[pgIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
t2 = PR_IntervalNow();
|
||||
|
||||
SM_FiniPageMgr(pm);
|
||||
|
||||
time = PR_IntervalToMilliseconds(t2 - t1);
|
||||
fprintf(stdout, "Time = %dms, live pages = %d\n", time, allocCount);
|
||||
fprintf(stdout, "Failures = %d, Avg failure size = %d\n", failed, failedSize/failed);
|
||||
fprintf(stdout, "Avg Time = %fms\n", (float)time / ITERATIONS);
|
||||
return 0;
|
||||
}
|
395
ef/gc/test/smtest.c
Normal file
395
ef/gc/test/smtest.c
Normal file
@ -0,0 +1,395 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
S P O R T M O D E L
|
||||
_____
|
||||
____/_____\____
|
||||
/__o____o____o__\ __
|
||||
\_______________/ (@@)/
|
||||
/\_____|_____/\ x~[]~
|
||||
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
||||
|
||||
Advanced Technology Garbage Collector
|
||||
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
||||
Author: Warren Harris
|
||||
*******************************************************************************/
|
||||
|
||||
#include "prinit.h"
|
||||
#include "sm.h"
|
||||
#include "smobj.h"
|
||||
#include "smpage.h"
|
||||
#include "smheap.h"
|
||||
#include "smtrav.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define K 1024
|
||||
#define M (K*K)
|
||||
#define PAGE (4*K)
|
||||
#define MIN_HEAP_SIZE (4*M)
|
||||
#define MAX_HEAP_SIZE (16*M)
|
||||
|
||||
FILE* out;
|
||||
int toFile = 1;
|
||||
int verbose = 0;
|
||||
int testLargeObjects = 1;
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
//PRUword dumpFlags = SMDumpFlag_Detailed | SMDumpFlag_GCState | SMDumpFlag_Abbreviate;
|
||||
PRUword dumpFlags = SMDumpFlag_Detailed;
|
||||
//PRUword dumpFlags = 0;
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
SMObjectClass* MyClass;
|
||||
SMObjectClass* MyLargeClass;
|
||||
SMArrayClass* MyArrayClass;
|
||||
|
||||
typedef struct MyClassStruct {
|
||||
SMObjStruct super;
|
||||
struct MyClassStruct* next;
|
||||
int value;
|
||||
// void* foo[2]; /* to see internal fragmentation */
|
||||
} MyClassStruct;
|
||||
|
||||
static void
|
||||
MyClass_finalize(SMObject* self)
|
||||
{
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
fprintf(out, "finalizing %d\n", SM_GET_FIELD(MyClassStruct, self, value));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
InitClasses(void)
|
||||
{
|
||||
SMFieldDesc* fds;
|
||||
MyClass = (SMObjectClass*)malloc(sizeof(SMObjectClass)
|
||||
+ (1 * sizeof(SMFieldDesc)));
|
||||
SM_InitObjectClass(MyClass, NULL, sizeof(SMObjectClass),
|
||||
sizeof(MyClassStruct), 1, 0);
|
||||
fds = SM_CLASS_GET_INST_FIELD_DESCS((SMClass*)MyClass);
|
||||
fds[0].offset = offsetof(MyClassStruct, next) / sizeof(void*);
|
||||
fds[0].count = 1;
|
||||
MyClass->data.finalize = MyClass_finalize;
|
||||
|
||||
MyLargeClass = (SMObjectClass*)malloc(sizeof(SMObjectClass)
|
||||
+ (1 * sizeof(SMFieldDesc)));
|
||||
SM_InitObjectClass(MyLargeClass, NULL, sizeof(SMObjectClass),
|
||||
4097, 1, 0);
|
||||
fds = SM_CLASS_GET_INST_FIELD_DESCS((SMClass*)MyLargeClass);
|
||||
fds[0].offset = offsetof(MyClassStruct, next) / sizeof(void*);
|
||||
fds[0].count = 1;
|
||||
MyLargeClass->data.finalize = MyClass_finalize;
|
||||
|
||||
MyArrayClass = (SMArrayClass*)malloc(sizeof(SMArrayClass));
|
||||
SM_InitArrayClass(MyArrayClass, NULL, (SMClass*)MyClass);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
MyClassStruct* root = NULL;
|
||||
|
||||
static void
|
||||
MarkRoots(SMGenNum genNum, PRBool copyCycle, void* data)
|
||||
{
|
||||
// SM_MarkThreadsConservatively(genNum, copyCycle, data); /* must be first */
|
||||
SM_MarkRoots((SMObject**)&root, 1, PR_FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
BeforeHook(SMGenNum genNum, PRUword collectCount,
|
||||
PRBool copyCycle, void* closure)
|
||||
{
|
||||
#ifdef xDEBUG
|
||||
FILE* out = (FILE*)closure;
|
||||
if (verbose) {
|
||||
fprintf(out, "Before collection %u gen %u %s\n", collectCount, genNum,
|
||||
(copyCycle ? "(copy cycle)" : ""));
|
||||
SM_DumpGCHeap(out, dumpFlags);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
AfterHook(SMGenNum genNum, PRUword collectCount,
|
||||
PRBool copyCycle, void* closure)
|
||||
{
|
||||
#ifdef xDEBUG
|
||||
FILE* out = (FILE*)closure;
|
||||
if (verbose) {
|
||||
fprintf(out, "After collection %u gen %u %s\n", collectCount, genNum,
|
||||
(copyCycle ? "(copy cycle)" : ""));
|
||||
SM_DumpGCHeap(out, dumpFlags);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ITERATIONS 10000
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
PRStatus status;
|
||||
SMArray* arr;
|
||||
MyClassStruct* obj;
|
||||
MyClassStruct* conserv;
|
||||
int i, cnt = 0;
|
||||
struct tm *newtime;
|
||||
time_t aclock;
|
||||
void* randomPtr;
|
||||
PRIntervalTime t1, t2;
|
||||
int failure = 0;
|
||||
|
||||
if (toFile) {
|
||||
out = fopen("smtest.out", "w+");
|
||||
if (out == NULL) {
|
||||
perror("Can't open smtest.out");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
out = stdout;
|
||||
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
SM_SetTraceOutputFile(out);
|
||||
#endif
|
||||
|
||||
time(&aclock);
|
||||
newtime = localtime(&aclock);
|
||||
fprintf(out, "SportModel Garbage Collector Test Program: %s\n", asctime(newtime));
|
||||
|
||||
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
|
||||
PR_SetThreadGCAble();
|
||||
status = SM_InitGC(MIN_HEAP_SIZE, MAX_HEAP_SIZE,
|
||||
BeforeHook, out,
|
||||
AfterHook, out,
|
||||
MarkRoots, NULL);
|
||||
if (status != PR_SUCCESS)
|
||||
return -1;
|
||||
|
||||
InitClasses();
|
||||
|
||||
t1 = PR_IntervalNow();
|
||||
|
||||
arr = SM_AllocArray(MyArrayClass, 13); /* gets collected */
|
||||
|
||||
randomPtr = (char*)SM_DECRYPT_ARRAY(arr) + (4096 * 200);
|
||||
|
||||
arr = SM_AllocArray(MyArrayClass, 13); /* try an array */
|
||||
SM_ARRAY_ELEMENTS(arr)[3] = (SMObject*)root;
|
||||
root = (MyClassStruct*)arr;
|
||||
arr = NULL;
|
||||
|
||||
SM_Collect();
|
||||
|
||||
if (testLargeObjects) {
|
||||
/* test large object allocation */
|
||||
obj = (MyClassStruct*)SM_AllocObject(MyLargeClass); /* gets collected */
|
||||
if (obj) {
|
||||
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "ALLOCATION FAILURE: Large Object\n");
|
||||
failure = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
obj = (MyClassStruct*)SM_AllocObject(MyLargeClass);
|
||||
if (obj) {
|
||||
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
||||
SM_SET_FIELD(MyClassStruct, obj, next, root);
|
||||
root = obj;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "ALLOCATION FAILURE: Large Object\n");
|
||||
failure = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
SM_Collect();
|
||||
}
|
||||
|
||||
obj = (MyClassStruct*)SM_AllocObject(MyClass); /* gets collected */
|
||||
if (obj) {
|
||||
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "ALLOCATION FAILURE: Small Object\n");
|
||||
failure = 1;
|
||||
goto done;
|
||||
}
|
||||
obj = (MyClassStruct*)SM_AllocObject(MyClass);
|
||||
if (obj) {
|
||||
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
||||
SM_SET_FIELD(MyClassStruct, obj, next, root); /* link this into the root list */
|
||||
root = obj;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "ALLOCATION FAILURE: Small Object\n");
|
||||
failure = 1;
|
||||
goto done;
|
||||
}
|
||||
conserv = (MyClassStruct*)SM_AllocObject(MyClass); /* keep a conservative root */
|
||||
((MyClassStruct*)SM_DECRYPT(conserv))->value = cnt++;
|
||||
|
||||
arr = SM_AllocArray(MyArrayClass, 13); /* try an array */
|
||||
SM_ARRAY_ELEMENTS(arr)[3] = (SMObject*)root;
|
||||
root = (MyClassStruct*)arr;
|
||||
arr = NULL;
|
||||
|
||||
obj = NULL;
|
||||
SM_Collect();
|
||||
SM_RunFinalization();
|
||||
|
||||
/* Force collection to occur for every subsequent object,
|
||||
for testing purposes. */
|
||||
#if 0
|
||||
SM_SetCollectThresholds(1*M/*sizeof(MyClassStruct)*/,
|
||||
sizeof(MyClassStruct),
|
||||
sizeof(MyClassStruct),
|
||||
sizeof(MyClassStruct),
|
||||
0);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < ITERATIONS; i++) {
|
||||
int size = rand() / 100 + 4;
|
||||
/* allocate some random garbage */
|
||||
// fprintf(out, "allocating array of size %d\n", size);
|
||||
arr = SM_AllocArray(MyArrayClass, size);
|
||||
if (arr) {
|
||||
SM_ARRAY_ELEMENTS(arr)[3] = (SMObject*)root;
|
||||
root = (MyClassStruct*)arr;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "ALLOCATION FAILURE: Array size %u index %d\n", size, i);
|
||||
/* continue */
|
||||
root = NULL;
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
SM_DumpPageMap(out, dumpFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
obj = (MyClassStruct*)SM_AllocObject(MyClass);
|
||||
if (obj) {
|
||||
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
||||
SM_SET_FIELD(MyClassStruct, obj, next, root); /* link this into the root list */
|
||||
root = obj;
|
||||
}
|
||||
else {
|
||||
fprintf(out, "ALLOCATION FAILURE: Small Object index %d\n", i);
|
||||
/* continue */
|
||||
root = NULL;
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
SM_DumpPageMap(out, dumpFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
//SM_DumpStats(out, dumpFlags);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
SM_Collect();
|
||||
}
|
||||
SM_RunFinalization();
|
||||
SM_Collect();
|
||||
SM_RunFinalization();
|
||||
|
||||
t2 = PR_IntervalNow();
|
||||
fprintf(out, "Test completed in %ums\n", PR_IntervalToMilliseconds(t2 - t1));
|
||||
|
||||
done:
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
SM_DumpPageMap(out, dumpFlags);
|
||||
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
||||
#endif
|
||||
|
||||
if (verbose) {
|
||||
/* list out all the objects */
|
||||
i = 0;
|
||||
obj = root;
|
||||
while (obj) {
|
||||
MyClassStruct* next;
|
||||
if (SM_IS_OBJECT((SMObject*)obj)) {
|
||||
fprintf(out, "%4d ", SM_GET_FIELD(MyClassStruct, obj, value));
|
||||
next = SM_GET_FIELD(MyClassStruct, obj, next);
|
||||
}
|
||||
else {
|
||||
fprintf(out, "[arr %p] ", obj);
|
||||
next = (MyClassStruct*)SM_ARRAY_ELEMENTS((SMArray*)obj)[3];
|
||||
}
|
||||
if (i++ % 20 == 19)
|
||||
fprintf(out, "\n");
|
||||
obj = next;
|
||||
}
|
||||
fprintf(out, "\nconserv = %d\n", SM_GET_FIELD(MyClassStruct, conserv, value));
|
||||
}
|
||||
|
||||
randomPtr = NULL;
|
||||
root = NULL;
|
||||
obj = NULL;
|
||||
conserv = NULL;
|
||||
|
||||
SM_Collect();
|
||||
SM_RunFinalization();
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
// SM_DumpPageMap(out, dumpFlags);
|
||||
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
||||
#endif
|
||||
|
||||
SM_Collect();
|
||||
SM_RunFinalization();
|
||||
#if 0
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
// SM_DumpPageMap(out, dumpFlags);
|
||||
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SM_Collect();
|
||||
SM_RunFinalization();
|
||||
#if 0
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
// SM_DumpPageMap(out, dumpFlags);
|
||||
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SM_Collect();
|
||||
SM_RunFinalization();
|
||||
#if defined(DEBUG) || defined(SM_DUMP)
|
||||
// SM_DumpStats(out, dumpFlags);
|
||||
SM_DumpPageMap(out, dumpFlags);
|
||||
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
||||
#endif
|
||||
|
||||
SM_CleanupGC(PR_TRUE);
|
||||
status = PR_Cleanup();
|
||||
if (status != PR_SUCCESS)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
94
ef/gc/todo.txt
Normal file
94
ef/gc/todo.txt
Normal file
@ -0,0 +1,94 @@
|
||||
SportModel To-Do List
|
||||
|
||||
- Make page manager initialize pages to all zeros
|
||||
- remove fancy object initialization from alloc routines
|
||||
- java needs all fields to be zeroed, not just pointers
|
||||
- need to zero objects when copying occurs too
|
||||
|
||||
- Implement weak ref support
|
||||
- new types
|
||||
- weak array
|
||||
- weak (open) hashtable
|
||||
- stack of objects with weak refs
|
||||
- on stack overflow, mark weak fields instead
|
||||
- if mark stack also overflows... !
|
||||
- routine to drain weak ref stack (done at end of gc)
|
||||
- if object, use weak ref field descriptors
|
||||
- if array, null out unmarked objects
|
||||
- need heuristic for disposing them
|
||||
- wait for java 1.2 spec for sun.misc.WeakRef
|
||||
|
||||
- Implement open hash-tables
|
||||
- needed?
|
||||
- must be primitive (with custom scavenge routine) because
|
||||
keys & values are intermixed in same object
|
||||
|
||||
- Sort out public vs. private info in header files
|
||||
|
||||
- Add ability to explicitly lock and unlock objects for JS
|
||||
|
||||
- Make Mac deal with multiple segments
|
||||
- i.e. grow page and card tables
|
||||
- need to stop app threads at safe points (non write barrier
|
||||
boundaries)
|
||||
- need to not be in a piece of code holding the address of a
|
||||
page desc (a lock in gc)
|
||||
|
||||
- Unix page manager
|
||||
|
||||
- 16 and 64 bit support
|
||||
|
||||
- Add splay trees to page manager to find clusters faster
|
||||
- profile for large-object intensive allocations
|
||||
|
||||
- Add blacklist support to page manager
|
||||
- blacklist bit already set for pages
|
||||
|
||||
- Figure out locking strategy
|
||||
- currently Collect0 is not protected!
|
||||
- need notion of "processor"?
|
||||
|
||||
- VirtualAlloc/Free on a per-page basis is too frequent
|
||||
- institute large blocks in the page manager?
|
||||
- add a better caching strategy
|
||||
- add a memory pressure api
|
||||
|
||||
- Change SMPage* to SMPageNum throughout
|
||||
- conservative references are being found to pages
|
||||
- these look like refs to the first object on the page
|
||||
|
||||
- Implement demographic feedback mediated tenuring policy
|
||||
|
||||
- Implement segmented arrays for win16
|
||||
- not needed anymore with Greg's 32-bit win3.1?
|
||||
|
||||
- Profiling
|
||||
- always more to do
|
||||
|
||||
- Memory visualization tools
|
||||
|
||||
- Add multi-processor support
|
||||
- introduce multiple youngest generations
|
||||
- need nspr notion of a "processor" to associate a youngest
|
||||
generation with (gating factor)
|
||||
- change allocation apis to specify a generation to allocate
|
||||
in
|
||||
- add allocation lock per youngest generation for concurrent
|
||||
allocation/collection
|
||||
- add lock for promotion to second generation during copying
|
||||
- fix write-barrier code to scan other youngest generation
|
||||
spaces for incoming pointers
|
||||
|
||||
- Integration with EF
|
||||
- on gcbranch2 of ns/electricalfire
|
||||
|
||||
- need build-time flag to determine whether class objects are
|
||||
scavenged or not
|
||||
- yes for EF
|
||||
|
||||
- need union type for pointer/non-pointer?
|
||||
- low-order tag bit for pointers
|
||||
- only 31 bits of data!
|
||||
- unify with dereference operation
|
||||
- ambiguous field descriptors in class
|
||||
|
Loading…
Reference in New Issue
Block a user