Adding SportModel, Warren Harris' partially complete garbage collector

code for the ElectricalFire JIT.
This commit is contained in:
fur%netscape.com 1998-12-17 07:00:17 +00:00
parent fc8d514fb4
commit 4a6bd744a3
58 changed files with 11202 additions and 0 deletions

35
ef/gc/Makefile Normal file
View 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
View 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 = &current->next;
current = current->next;
*lastOfNew = NULL;
}
else {
/* leave it on the old chain */
previous = &current->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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
include manifest.mn
include $(DEPTH)/config/rules.mk

49
ef/gc/include/blank.h Normal file
View 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__ */
/******************************************************************************/

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

View File

@ -0,0 +1,4 @@
#define DEBUG 1
#undef NDEBUG
#include "NSPRConfig.h"

View File

@ -0,0 +1,4 @@
#undef DEBUG
#define NDEBUG 1
#include "NSPRConfig.h"

BIN
ef/gc/macbuild/buckets.prj2 Normal file

Binary file not shown.

BIN
ef/gc/macbuild/divtest.prj2 Normal file

Binary file not shown.

Binary file not shown.

BIN
ef/gc/macbuild/maltest.prj2 Normal file

Binary file not shown.

BIN
ef/gc/macbuild/smtest.prj2 Normal file

Binary file not shown.

35
ef/gc/makefile.win Normal file
View 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
View File

@ -0,0 +1,4 @@
DEPTH = ../..
include manifest.mn
include $(DEPTH)/config/rules.mk

70
ef/gc/src/makefile.win Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

124
ef/gc/src/smgen.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
DEPTH = ../..
include manifest.mn
include $(DEPTH)/config/rules.mk

63
ef/gc/test/alloc.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,10 @@
MODULE = sm
DEPTH = ../..
CSRCS = \
maltest.c \
$(NULL)
REQUIRES = sm nspr20

656
ef/gc/test/msgctest.c Normal file
View 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
View 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
View 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
View 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