From 6fdf35b4ee30a72735765140ee0497ff78a69ecf Mon Sep 17 00:00:00 2001 From: "fur%netscape.com" Date: Thu, 17 Dec 1998 07:00:17 +0000 Subject: [PATCH] Adding SportModel, Warren Harris' partially complete garbage collector code for the ElectricalFire JIT. git-svn-id: svn://10.0.0.236/trunk@16558 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/ef/gc/Makefile | 35 + mozilla/ef/gc/hash/dynahash.c | 645 +++++++++++++ mozilla/ef/gc/hash/dynahash.h | 165 ++++ mozilla/ef/gc/hash/openhash.c | 128 +++ mozilla/ef/gc/hash/openhash.h | 101 +++ mozilla/ef/gc/hash/plhash.c | 504 +++++++++++ mozilla/ef/gc/hash/plhash.h | 145 +++ mozilla/ef/gc/include/Makefile | 2 + mozilla/ef/gc/include/blank.h | 49 + mozilla/ef/gc/include/makefile.win | 9 + mozilla/ef/gc/include/manifest.mn | 16 + mozilla/ef/gc/include/sm.h | 353 ++++++++ mozilla/ef/gc/include/smgen.h | 114 +++ mozilla/ef/gc/include/smhash.h | 87 ++ mozilla/ef/gc/include/smheap.h | 327 +++++++ mozilla/ef/gc/include/smmalloc.h | 72 ++ mozilla/ef/gc/include/smobj.h | 355 ++++++++ mozilla/ef/gc/include/smpage.h | 282 ++++++ mozilla/ef/gc/include/smpool.h | 320 +++++++ mozilla/ef/gc/include/smpriv.h | 216 +++++ mozilla/ef/gc/include/smstack.h | 112 +++ mozilla/ef/gc/include/smtrav.h | 111 +++ mozilla/ef/gc/macbuild/SM.prj2 | Bin 0 -> 665 bytes mozilla/ef/gc/macbuild/SMDebug.Prefix | 4 + mozilla/ef/gc/macbuild/SMRelease.Prefix | 4 + mozilla/ef/gc/macbuild/buckets.prj2 | Bin 0 -> 665 bytes mozilla/ef/gc/macbuild/divtest.prj2 | Bin 0 -> 665 bytes mozilla/ef/gc/macbuild/hashtest.prj2 | Bin 0 -> 603 bytes mozilla/ef/gc/macbuild/maltest.prj2 | Bin 0 -> 665 bytes mozilla/ef/gc/macbuild/smtest.prj2 | Bin 0 -> 665 bytes mozilla/ef/gc/makefile.win | 35 + mozilla/ef/gc/src/Makefile | 4 + mozilla/ef/gc/src/makefile.win | 70 ++ mozilla/ef/gc/src/manifest.mn | 22 + mozilla/ef/gc/src/smalloc.c | 300 ++++++ mozilla/ef/gc/src/smassert.c | 101 +++ mozilla/ef/gc/src/smgc.c | 1106 +++++++++++++++++++++++ mozilla/ef/gc/src/smgen.c | 124 +++ mozilla/ef/gc/src/smhash.c | 188 ++++ mozilla/ef/gc/src/smheap.c | 523 +++++++++++ mozilla/ef/gc/src/smobj.c | 100 ++ mozilla/ef/gc/src/smpage.c | 670 ++++++++++++++ mozilla/ef/gc/src/smpool.c | 660 ++++++++++++++ mozilla/ef/gc/src/smstack.c | 123 +++ mozilla/ef/gc/src/smtrav.c | 456 ++++++++++ mozilla/ef/gc/test/Makefile | 4 + mozilla/ef/gc/test/alloc.c | 63 ++ mozilla/ef/gc/test/buckets.c | 71 ++ mozilla/ef/gc/test/divtest.c | 112 +++ mozilla/ef/gc/test/hashtable.cpp | 457 ++++++++++ mozilla/ef/gc/test/hashtest.c | 372 ++++++++ mozilla/ef/gc/test/makefile.win | 34 + mozilla/ef/gc/test/maltest.c | 175 ++++ mozilla/ef/gc/test/manifest.mn | 10 + mozilla/ef/gc/test/msgctest.c | 656 ++++++++++++++ mozilla/ef/gc/test/pagetest.c | 121 +++ mozilla/ef/gc/test/smtest.c | 395 ++++++++ mozilla/ef/gc/todo.txt | 94 ++ 58 files changed, 11202 insertions(+) create mode 100644 mozilla/ef/gc/Makefile create mode 100644 mozilla/ef/gc/hash/dynahash.c create mode 100644 mozilla/ef/gc/hash/dynahash.h create mode 100644 mozilla/ef/gc/hash/openhash.c create mode 100644 mozilla/ef/gc/hash/openhash.h create mode 100644 mozilla/ef/gc/hash/plhash.c create mode 100644 mozilla/ef/gc/hash/plhash.h create mode 100644 mozilla/ef/gc/include/Makefile create mode 100644 mozilla/ef/gc/include/blank.h create mode 100644 mozilla/ef/gc/include/makefile.win create mode 100644 mozilla/ef/gc/include/manifest.mn create mode 100644 mozilla/ef/gc/include/sm.h create mode 100644 mozilla/ef/gc/include/smgen.h create mode 100644 mozilla/ef/gc/include/smhash.h create mode 100644 mozilla/ef/gc/include/smheap.h create mode 100644 mozilla/ef/gc/include/smmalloc.h create mode 100644 mozilla/ef/gc/include/smobj.h create mode 100644 mozilla/ef/gc/include/smpage.h create mode 100644 mozilla/ef/gc/include/smpool.h create mode 100644 mozilla/ef/gc/include/smpriv.h create mode 100644 mozilla/ef/gc/include/smstack.h create mode 100644 mozilla/ef/gc/include/smtrav.h create mode 100644 mozilla/ef/gc/macbuild/SM.prj2 create mode 100644 mozilla/ef/gc/macbuild/SMDebug.Prefix create mode 100644 mozilla/ef/gc/macbuild/SMRelease.Prefix create mode 100644 mozilla/ef/gc/macbuild/buckets.prj2 create mode 100644 mozilla/ef/gc/macbuild/divtest.prj2 create mode 100644 mozilla/ef/gc/macbuild/hashtest.prj2 create mode 100644 mozilla/ef/gc/macbuild/maltest.prj2 create mode 100644 mozilla/ef/gc/macbuild/smtest.prj2 create mode 100644 mozilla/ef/gc/makefile.win create mode 100644 mozilla/ef/gc/src/Makefile create mode 100644 mozilla/ef/gc/src/makefile.win create mode 100644 mozilla/ef/gc/src/manifest.mn create mode 100644 mozilla/ef/gc/src/smalloc.c create mode 100644 mozilla/ef/gc/src/smassert.c create mode 100644 mozilla/ef/gc/src/smgc.c create mode 100644 mozilla/ef/gc/src/smgen.c create mode 100644 mozilla/ef/gc/src/smhash.c create mode 100644 mozilla/ef/gc/src/smheap.c create mode 100644 mozilla/ef/gc/src/smobj.c create mode 100644 mozilla/ef/gc/src/smpage.c create mode 100644 mozilla/ef/gc/src/smpool.c create mode 100644 mozilla/ef/gc/src/smstack.c create mode 100644 mozilla/ef/gc/src/smtrav.c create mode 100644 mozilla/ef/gc/test/Makefile create mode 100644 mozilla/ef/gc/test/alloc.c create mode 100644 mozilla/ef/gc/test/buckets.c create mode 100644 mozilla/ef/gc/test/divtest.c create mode 100644 mozilla/ef/gc/test/hashtable.cpp create mode 100644 mozilla/ef/gc/test/hashtest.c create mode 100644 mozilla/ef/gc/test/makefile.win create mode 100644 mozilla/ef/gc/test/maltest.c create mode 100644 mozilla/ef/gc/test/manifest.mn create mode 100644 mozilla/ef/gc/test/msgctest.c create mode 100644 mozilla/ef/gc/test/pagetest.c create mode 100644 mozilla/ef/gc/test/smtest.c create mode 100644 mozilla/ef/gc/todo.txt diff --git a/mozilla/ef/gc/Makefile b/mozilla/ef/gc/Makefile new file mode 100644 index 00000000000..f33c2abf257 --- /dev/null +++ b/mozilla/ef/gc/Makefile @@ -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/ diff --git a/mozilla/ef/gc/hash/dynahash.c b/mozilla/ef/gc/hash/dynahash.c new file mode 100644 index 00000000000..2c4e1485669 --- /dev/null +++ b/mozilla/ef/gc/hash/dynahash.c @@ -0,0 +1,645 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/******************************************************************************* + * Hash Tables: Based on Larson's in-memory adaption of linear hashing + * [CACM, vol 31, #4, 1988 pp 446-457] + ******************************************************************************/ + +#include "dynahash.h" +#include "prlog.h" + +#define MUL(x, y) ((x) << (y##_SHIFT)) +#define DIV(x, y) ((x) >> (y##_SHIFT)) +#define MOD(x, y) ((x) & ((y) - 1)) + +PR_IMPLEMENT(PLDynahash*) +PL_NewDynahash(PRUint32 initialSize, + PLHashFun hash, PLEqualFun equal, + PLDestroyFun del, void* context) +{ + PRStatus status; + PLDynahash* self = (PLDynahash*)malloc(sizeof(PLDynahash)); + if (self == NULL) + return self; + status = PL_DynahashInit(self, initialSize, hash, equal, del, context); + if (status != PR_SUCCESS) { + PL_DynahashDestroy(self); + return NULL; + } + return self; +} + +PR_IMPLEMENT(void) +PL_DynahashDestroy(PLDynahash* self) +{ + PL_DynahashEmpty(self); + free(self); +} + +PR_IMPLEMENT(PRStatus) +PL_DynahashInit(PLDynahash* self, PRUint32 initialSize, PLHashFun hash, + PLEqualFun equal, PLDestroyFun del, void* context) +{ + PRUint32 count = DIV(initialSize, PL_DYNAHASH_BUCKETSIZE) + 1; + self->directory = (PLHashDir*)count; + self->hash = hash; + self->equal = equal; + self->destroy = del; + self->keyCount = 0; + self->nextBucketToSplit = 0; + self->maxSize = (PRUint16)MUL(count, PL_DYNAHASH_BUCKETSIZE); + self->bucketCount = 0; + self->context = context; +#ifdef PL_DYNAHASH_STATISTICS + self->accesses = 0; + self->collisions = 0; + self->expansions = 0; +#endif /* PL_DYNAHASH_STATISTICS */ + return PR_SUCCESS; +} + +static PRStatus +pl_DynahashAllocBucket(PLDynahash* self) +{ + PLHashDir* newDir; + PRUint32 i, size; + PRUint16 count = 1; + + if (self->bucketCount == 0) { + count = (PRUint16)(PRInt32)self->directory; + } + + /* realloc directory */ + size = self->bucketCount + count; + newDir = (PLHashDir*)calloc(sizeof(PLHashDir*), size); + if (newDir == NULL) + return PR_FAILURE; +// memset((void*)newDir, 0, size * sizeof(PLHashBucket*)); + if (self->bucketCount) { + memcpy((void*)newDir, self->directory, self->bucketCount + * sizeof(PLHashBucket*)); + } + + /* add new bucket(s) */ + for (i = self->bucketCount; i < size; i++) { + PLHashBucket* newBucket; + newBucket = (PLHashBucket*)calloc(sizeof(PLHashBucket*), PL_DYNAHASH_BUCKETSIZE); + if (newBucket == NULL) return PR_FAILURE; +// memset((void*)newBucket, 0, sizeof(PLDynahashElement*) +// * PL_DYNAHASH_BUCKETSIZE); + newDir[i] = newBucket; + } + + if (self->bucketCount != 0) { + free(self->directory); + } + self->directory = newDir; + self->bucketCount += count; + return PR_SUCCESS; +} + +PR_IMPLEMENT(void) +PL_DynahashCleanup(PLDynahash* self) +{ + PL_DynahashEmpty(self); +} + +#define PL_DYNAHASH_HASH(resultVar, self, key) \ +{ \ + PRUword _h_ = self->hash(self->context, key); \ + PRUword _a_ = MOD(_h_, self->maxSize); \ + if (_a_ < self->nextBucketToSplit) { \ + _a_ = MOD(_h_, self->maxSize << 1); /* h % (2 * maxSize) */ \ + } \ + resultVar = _a_; \ +} \ + +PR_IMPLEMENT(PLDynahashElement*) +PL_DynahashLookup(PLDynahash* self, PLDynahashElement* element) +{ + PRUword h; + PRUint32 bucketIndex, binIndex; + PLHashBucket* bin; + PLDynahashElement* result = NULL; + PLHashDir* dir; + PLDynahashElement* e; + + if (self->bucketCount == 0) return NULL; + + PL_DYNAHASH_HASH(h, self, element); + bucketIndex = DIV(h, PL_DYNAHASH_BUCKETSIZE); + binIndex = MOD(h, PL_DYNAHASH_BUCKETSIZE); + + /* hash ensures valid segment */ + dir = self->directory; + bin = dir[bucketIndex]; + PR_ASSERT(bin); + + for (e = bin[binIndex]; e != NULL; e = e->next) { + if (self->equal(self->context, e, element)) { + return e; + } + } + + return result; +} + +static PRStatus +pl_DynahashExpand(PLDynahash* self) +{ + PRStatus err; + if (self->maxSize + self->nextBucketToSplit + < MUL(PL_DYNAHASH_MAX_DIR_SIZE, PL_DYNAHASH_BUCKETSIZE)) { + PRUint32 oldDirIndex, oldBinIndex, newDirIndex, newBinIndex; + PLHashBucket* oldSegment; + PLHashBucket* newSegment; + PRUword newAddress; + PLDynahashElement** previous; + PLDynahashElement* current; + PLDynahashElement** lastOfNew; + + /* locate the bucket to split */ + oldDirIndex = DIV(self->nextBucketToSplit, PL_DYNAHASH_BUCKETSIZE); + oldSegment = self->directory[oldDirIndex]; + oldBinIndex = MOD(self->nextBucketToSplit, PL_DYNAHASH_BUCKETSIZE); + + /* expand address space */ + newAddress = self->maxSize + self->nextBucketToSplit; + newDirIndex = DIV(newAddress, PL_DYNAHASH_BUCKETSIZE); + newBinIndex = MOD(newAddress, PL_DYNAHASH_BUCKETSIZE); + + /* we need to unlock the directory handle here in case AllocBucket grows + the directory */ + if (newBinIndex == 0) { + err = pl_DynahashAllocBucket(self); + if (err != PR_SUCCESS) return err; + } + PR_ASSERT(newDirIndex < self->bucketCount); + + newSegment = self->directory[newDirIndex]; + + /* adjust state */ + self->nextBucketToSplit++; + if (self->nextBucketToSplit == self->maxSize) { + self->maxSize <<= 1; /* maxSize *= 2 */ + self->nextBucketToSplit = 0; + } + + /* relocate records to the new bucket */ + previous = &oldSegment[oldBinIndex]; + current = *previous; + lastOfNew = &newSegment[newBinIndex]; + *lastOfNew = NULL; + while (current != NULL) { + PRUword hash; + PL_DYNAHASH_HASH(hash, self, current); + if (hash == newAddress) { + /* attach to the end of the new chain */ + *lastOfNew = current; + /* remove from old chain */ + *previous = current->next; + lastOfNew = ¤t->next; + current = current->next; + *lastOfNew = NULL; + } + else { + /* leave it on the old chain */ + previous = ¤t->next; + current = current->next; + } + + } + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +PL_DynahashAdd(PLDynahash* self, PLDynahashElement* element, PRBool replace, + PLDynahashElement* *elementToDestroy) +{ + /* Returns a pointer to the unused thing (either the key or the replaced key). */ + PRStatus err = PR_SUCCESS; + PRUword h; + PRUint32 bucketIndex, binIndex; + PLHashBucket* b; + PLDynahashElement** p; + PLDynahashElement* q; + PLDynahashElement* n; + + *elementToDestroy = NULL; + if (self->bucketCount == 0) { + err = pl_DynahashAllocBucket(self); + if (err != PR_SUCCESS) return err; + } + + PL_DYNAHASH_HASH(h, self, element); + bucketIndex = DIV(h, PL_DYNAHASH_BUCKETSIZE); + binIndex = MOD(h, PL_DYNAHASH_BUCKETSIZE); + + /* hash ensures valid segment */ + b = self->directory[bucketIndex]; + PR_ASSERT(b); + + /* follow collision chain */ + p = &b[binIndex]; + q = *p; + + while (q && !self->equal(self->context, q, element)) { + p = &q->next; + q = *p; + } + if (q && !replace) { + *elementToDestroy = element; + goto done; + } + + n = element; + *p = n; + if (q) { + /* replace q with n */ + n->next = q->next; + } + else { + /* table over full? expand it: */ + if (++self->keyCount / MUL(self->bucketCount, PL_DYNAHASH_BUCKETSIZE) + > PL_DYNAHASH_LOAD_FACTOR) { + err = pl_DynahashExpand(self); + if (err != PR_SUCCESS) return err; + } + } + *elementToDestroy = q; + done:; + return err; +} + +PR_IMPLEMENT(PLDynahashElement*) +PL_DynahashRemove(PLDynahash* self, PLDynahashElement* element) +{ + PRUword h; + PRUint32 bucketIndex, binIndex; + PLHashBucket* b; + PLDynahashElement** p; + PLDynahashElement* q; + + if (self->bucketCount == 0) + return NULL; + + PL_DYNAHASH_HASH(h, self, element); + bucketIndex = DIV(h, PL_DYNAHASH_BUCKETSIZE); + binIndex = MOD(h, PL_DYNAHASH_BUCKETSIZE); + b = self->directory[bucketIndex]; + PR_ASSERT(b); + + p = &b[binIndex]; + q = *p; + while (q && !self->equal(self->context, q, element)) { + p = &q->next; + q = *p; + } + if (q) { + /* remove q */ + *p = q->next; + --self->keyCount; + /* XXX table can be shrunk? shrink it */ + } + return q; +} + +static PLDynahashElement* +PL_DynahashRemove1(PLDynahash* self, PRUint32 bucketIndex, PRInt32 binIndex, + int elemIndex) +{ + /* only used by PLDynahashIterator, below */ + PLHashBucket* b; + PLDynahashElement** p; + PLDynahashElement* q; + + if (self->bucketCount == 0) + return NULL; + + b = self->directory[bucketIndex]; + p = &b[binIndex]; + q = *p; + + while (elemIndex--) { + p = &q->next; + q = *p; + } + if (q) { + *p = q->next; + --self->keyCount; + } + return q; +} + +PR_IMPLEMENT(void) +PL_DynahashEmpty(PLDynahash* self) +{ + PRUint32 i, j; + for (i = 0; i < self->bucketCount; i++) { + PLHashBucket* b = self->directory[i]; + for (j = 0; j < PL_DYNAHASH_BUCKETSIZE; j++) { + PLDynahashElement* q; + PLDynahashElement* p = b[j]; + while (p) { + q = p->next; + self->destroy(self->context, p); + p = q; + } + } + free(b); + } + if (self->bucketCount != 0) { + free(self->directory); + } + self->keyCount = 0; + self->nextBucketToSplit = 0; + self->bucketCount = 0; + self->directory = (PLHashDir*)1; + self->maxSize = MUL(1, PL_DYNAHASH_BUCKETSIZE); +} + +#ifdef DEBUG + +PR_IMPLEMENT(PRBool) +PL_DynahashIsValid(PLDynahash* self) +{ + PLDynahashElement* hep; + PLDynahashIterator iter; + PL_DynahashIteratorInit(&iter, self); + while ((hep = PL_DynahashIteratorNext(&iter)) != NULL) { + if (PL_DynahashLookup(self, hep) != hep) { + /* something else matches */ + return PR_FALSE; + } + } + return PR_TRUE; +} + +#endif /* DEBUG */ + +/******************************************************************************* + * Hash Table Iterator + ******************************************************************************/ + +PR_IMPLEMENT(void) +PL_DynahashIteratorInit(PLDynahashIterator* self, PLDynahash* ht) +{ + self->table = ht; + self->bucket = 0; + self->bin = -1; + self->curno = 0; + self->lbucket = 0; + self->lbin = 0; + self->lcurno = 0; + self->nextp = NULL; + self->curp = NULL; + if (ht->bucketCount == 0) { + self->done = PR_TRUE; + self->b = NULL; + } + else { + self->done = PR_FALSE; + self->b = ht->directory[self->bucket]; + PL_DynahashIteratorNext(self); /* setup nextp */ + } +} + +static void +pl_DynahashIteratorNext1(PLDynahashIterator* self) +{ + if (!self->done) { + /* save last position in case we have to reset */ + self->lbucket = self->bucket; + self->lbin = self->bin; + self->lcurno = self->curno; + if (self->nextp) { + self->nextp = self->nextp->next; + self->curno++; + } + while (!self->nextp) { + if (++self->bin >= PL_DYNAHASH_BUCKETSIZE) { + self->bin = 0; + if (++self->bucket < self->table->bucketCount) { + self->b = self->table->directory[self->bucket]; + } + else { + self->done = PR_TRUE; + break; + } + } + self->nextp = self->b[self->bin]; + self->curno = 0; + } + } +} + +PR_IMPLEMENT(PLDynahashElement*) +PL_DynahashIteratorNext(PLDynahashIterator* self) +{ + /* This iterator precomputes one next element ahead so that we can detect + when we dropped the current element and not skip elements. */ + if (self->curp != self->nextp) { + self->done = PR_TRUE; + } + else { + pl_DynahashIteratorNext1(self); + } + return self->curp; +} + +PR_IMPLEMENT(PRUword) +PL_DynahashIteratorCurrentAddress(PLDynahashIterator* self) +{ + return (PRUword)(MUL(self->lbucket, PL_DYNAHASH_BUCKETSIZE) + self->lbin); +} + +static PLDynahashElement* +PL_DynahashIteratorRemoveCurrent(PLDynahashIterator* self) +{ + PLDynahashElement* dp = PL_DynahashRemove1(self->table, + self->lbucket, + self->lbin, + (int) self->lcurno); + if (dp != NULL) { + if ((self->bin == self->lbin) && (self->bucket == self->lbucket)) { + self->curno = self->lcurno; + } + } + return dp; +} + +PR_IMPLEMENT(void) +PL_DynahashIteratorReset(PLDynahashIterator* self, PLDynahash* ht) +{ + PRInt32 i; + + self->table = ht; + + if (self->done) return; + + if (self->lbin == -1) { + /* a single next has occurred, will emit first element twice */ + self->lbin = 0; + } + + /* recompute curp from lbucket/lbin/lcurno */ + if (self->lbucket >= self->table->bucketCount) { + /* we're done */ + self->curp = self->nextp = 0; + self->done = PR_TRUE; + return; + } + self->b = self->table->directory[self->lbucket]; + self->curp = self->b[self->lbin]; + for (i = 0; i < self->lcurno && self->curp; i++) { + self->curp = self->curp->next; + } + /* nextp also cannot be trusted */ + self->bucket = self->lbucket; + self->bin = self->lbin; + self->curno = self->lcurno; + + if (self->curp == self->nextp) { + /* special case where curp has been destroyed and nextp was the next one + in the bin */ + return; + } + + /* curp mibht be zero, it doesn't matter */ + self->nextp = self->curp; + pl_DynahashIteratorNext1(self); +} + +/******************************************************************************* + * Test + ******************************************************************************/ + +#ifdef DEBUG + +typedef struct MyHashElement { + PLDynahashElement hashElement; /* must be first */ + PRUint32 key; + PRUint32 value; +} MyHashElement; + +PRUint32 +Dynahash_Test_Hash(void* context, MyHashElement* x) +{ +#ifdef XP_MAC +#pragma unused(context) +#endif + return x->key; +} + +PRBool +Dynahash_Test_equal(void* context, MyHashElement* x, MyHashElement* y) +{ +#ifdef XP_MAC +#pragma unused(context) +#endif + return (PRBool)(x->key == y->key); +} + +void +Dynahash_Test_destroy(void* context, MyHashElement* x) +{ +#ifdef XP_MAC +#pragma unused(context) +#endif + free(x); +} + +#define kDynahash_Test_Iterations 1000 + +#include + +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 */ + +/******************************************************************************/ diff --git a/mozilla/ef/gc/hash/dynahash.h b/mozilla/ef/gc/hash/dynahash.h new file mode 100644 index 00000000000..b48c2b8444e --- /dev/null +++ b/mozilla/ef/gc/hash/dynahash.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/hash/openhash.c b/mozilla/ef/gc/hash/openhash.c new file mode 100644 index 00000000000..89e9b045de7 --- /dev/null +++ b/mozilla/ef/gc/hash/openhash.c @@ -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++; + } +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/hash/openhash.h b/mozilla/ef/gc/hash/openhash.h new file mode 100644 index 00000000000..66cd7ff0ddb --- /dev/null +++ b/mozilla/ef/gc/hash/openhash.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/hash/plhash.c b/mozilla/ef/gc/hash/plhash.c new file mode 100644 index 00000000000..beccf2b38f3 --- /dev/null +++ b/mozilla/ef/gc/hash/plhash.c @@ -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 +#include + +/* 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 +#include + +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; +} diff --git a/mozilla/ef/gc/hash/plhash.h b/mozilla/ef/gc/hash/plhash.h new file mode 100644 index 00000000000..f3b52207ad7 --- /dev/null +++ b/mozilla/ef/gc/hash/plhash.h @@ -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 +#include +#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___ */ diff --git a/mozilla/ef/gc/include/Makefile b/mozilla/ef/gc/include/Makefile new file mode 100644 index 00000000000..76082808015 --- /dev/null +++ b/mozilla/ef/gc/include/Makefile @@ -0,0 +1,2 @@ +include manifest.mn +include $(DEPTH)/config/rules.mk diff --git a/mozilla/ef/gc/include/blank.h b/mozilla/ef/gc/include/blank.h new file mode 100644 index 00000000000..539081d82c0 --- /dev/null +++ b/mozilla/ef/gc/include/blank.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/makefile.win b/mozilla/ef/gc/include/makefile.win new file mode 100644 index 00000000000..25a40f0018d --- /dev/null +++ b/mozilla/ef/gc/include/makefile.win @@ -0,0 +1,9 @@ +#//------------------------------------------------------------------------ +#// +#// Makefile to install MODULES/APPLET/INCLUDE header files into the distribution +#// directory. +#// +#//------------------------------------------------------------------------ +include +include <$(DEPTH)/config/rules.mak> + diff --git a/mozilla/ef/gc/include/manifest.mn b/mozilla/ef/gc/include/manifest.mn new file mode 100644 index 00000000000..bc67f85bbd6 --- /dev/null +++ b/mozilla/ef/gc/include/manifest.mn @@ -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 diff --git a/mozilla/ef/gc/include/sm.h b/mozilla/ef/gc/include/sm.h new file mode 100644 index 00000000000..e74fc8c62ae --- /dev/null +++ b/mozilla/ef/gc/include/sm.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smgen.h b/mozilla/ef/gc/include/smgen.h new file mode 100644 index 00000000000..f1983b8ab88 --- /dev/null +++ b/mozilla/ef/gc/include/smgen.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smhash.h b/mozilla/ef/gc/include/smhash.h new file mode 100644 index 00000000000..54d21bd64dc --- /dev/null +++ b/mozilla/ef/gc/include/smhash.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smheap.h b/mozilla/ef/gc/include/smheap.h new file mode 100644 index 00000000000..cbda35ed019 --- /dev/null +++ b/mozilla/ef/gc/include/smheap.h @@ -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 + +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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smmalloc.h b/mozilla/ef/gc/include/smmalloc.h new file mode 100644 index 00000000000..a019e0fafeb --- /dev/null +++ b/mozilla/ef/gc/include/smmalloc.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smobj.h b/mozilla/ef/gc/include/smobj.h new file mode 100644 index 00000000000..b29f493adf6 --- /dev/null +++ b/mozilla/ef/gc/include/smobj.h @@ -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 + +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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smpage.h b/mozilla/ef/gc/include/smpage.h new file mode 100644 index 00000000000..d3cb9437671 --- /dev/null +++ b/mozilla/ef/gc/include/smpage.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smpool.h b/mozilla/ef/gc/include/smpool.h new file mode 100644 index 00000000000..6e6c7bfe3cd --- /dev/null +++ b/mozilla/ef/gc/include/smpool.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smpriv.h b/mozilla/ef/gc/include/smpriv.h new file mode 100644 index 00000000000..699841d6981 --- /dev/null +++ b/mozilla/ef/gc/include/smpriv.h @@ -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 /* for memset */ +#include + +#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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smstack.h b/mozilla/ef/gc/include/smstack.h new file mode 100644 index 00000000000..8d4382a51db --- /dev/null +++ b/mozilla/ef/gc/include/smstack.h @@ -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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/include/smtrav.h b/mozilla/ef/gc/include/smtrav.h new file mode 100644 index 00000000000..df365b11823 --- /dev/null +++ b/mozilla/ef/gc/include/smtrav.h @@ -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 + +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__ */ +/******************************************************************************/ diff --git a/mozilla/ef/gc/macbuild/SM.prj2 b/mozilla/ef/gc/macbuild/SM.prj2 new file mode 100644 index 0000000000000000000000000000000000000000..51a68a51d328f840c80add1bbe4f50ed9b1a8798 GIT binary patch literal 665 zcmd^+yGq1B6ozMJUH9Uu>t$DMbVWfCE5Taa2x<{r)*E()WMD^P29i<1#=_EP5L-)2 z@d2!D>;&Jy#>Ubws88X6pYLOG5>DobVzVTqkbV%F#M!LZ`YEA=;9Q6mA(s}Qf=8f+ zSA<;FupU|)!vd{~LQd}pIddUI^?|cgs{Hkz@@%9JgETcZZFN$6qC@xh=s!`0^X)c2 zJWKNX&1;PmvR=6Zk4ay_e7wLbQmn2=5xgb+9g}q76Db89Yv3Cx``a$x#TTi#uFlv+ z1F!lzIm1`+0q5BS-^XhxmoHE8L7Qn0zrlx09Or(I5AXIYdz8<3eal3AU%ulbzCFiP zKKtmVcJ!+`d<U-iS>;o4UrEzx{Xkg!A^8Up?ZJEf!JF6oxq_z@b(>5muu66GtKzK`FcLR&{2{0SA^wqe)uhDy#(8sJ~3oOXhQ zyQskxCx}krHFz|&O$6`5V<5}7PT+Bq1U7sPPv|hrIJ>*a-L5%@pW*7Z_W8c`4o^Ae z3|3k8=~j?3ua@8$kZfuTp1lZc1G{i7)LAxl1=oM)@8G%A-Xp(ygy+-nfLZklFX+f7 IoBD)*02>TPw*UYD literal 0 HcmV?d00001 diff --git a/mozilla/ef/gc/macbuild/divtest.prj2 b/mozilla/ef/gc/macbuild/divtest.prj2 new file mode 100644 index 0000000000000000000000000000000000000000..a748d32510ca9c788c5da6111af2a5013fef276b GIT binary patch literal 665 zcmd^+Jxjw-6oyZ3?WbS$qgA(x4$gvulh_E=AyDeqYDfaTHReJR6f!JF6oxq_z@b(>5muu66GtKzK`FcLR&{2{0SA^wqe)uhDy#(8sJ~3oOXhQ zyQskxCx}krHFz|&O$6`5V<5}7PT+Bq1U7sPPv|hrIJ>*a-L5%@pW*7Z_W8c`4o^Ae z3|3k8=~j?3ua@8$kZfuTp1lZc1G{i7)LAxl1=oM)@8G%A-Xp(ygy+-nfLZklFX+f7 IoBD)*02>TPw*UYD literal 0 HcmV?d00001 diff --git a/mozilla/ef/gc/macbuild/hashtest.prj2 b/mozilla/ef/gc/macbuild/hashtest.prj2 new file mode 100644 index 0000000000000000000000000000000000000000..9dc1e5605f8797a25f2e8a47348a9cdd2a7148e9 GIT binary patch literal 603 zcmd_nyGq1B7>410Cc3Wc`LG^3MGJ4hQpAn0S_Hy+Y7NQ2j>HUPM+F-TORqs}y#(h}yr)uuRicp=vQhx^R0 UAMk?D4_T|<@S-h3X47Bz2d!H^KmY&$ literal 0 HcmV?d00001 diff --git a/mozilla/ef/gc/macbuild/maltest.prj2 b/mozilla/ef/gc/macbuild/maltest.prj2 new file mode 100644 index 0000000000000000000000000000000000000000..0fa50704f2d85b08f8b93ec39bfaf2597f893cf9 GIT binary patch literal 665 zcmd_nze>YU6vy$CTl?34ZM9XmiVn_#;3hUgbqIv|Z#5)=UWvKTL$^Iw zZ(J6+uaQ;Xn25~LUl6&jirg%V6t3uXER{VzShfkmU9aEQrr&b=W4bN27XH+$w7KjG@S4tT%yhHH*F z!YZ4+-U(Cot2sOYnN6+X$&=7F*u#xTXS1m@-2B~tiKkL~i}}?9o=&43_No^=qhp)d H)F=J{s{%(7 literal 0 HcmV?d00001 diff --git a/mozilla/ef/gc/macbuild/smtest.prj2 b/mozilla/ef/gc/macbuild/smtest.prj2 new file mode 100644 index 0000000000000000000000000000000000000000..45ac985dba02fb2d2f4d7e25c31ff212701ef67b GIT binary patch literal 665 zcmd_nyGz4R6vy$CTl@4?AFXz)=-@0kIEjr=9Rj7kRzni#)tC!ORB&<7&B;GN9PL)z z#6Q5<#ZB-(aB*?;8>)YW7Y@HqZcf6!)@6|gEz-XgDfHQETPk~exNPGG2W}i|6E{1tISRb=@78}x%Uf!- z_}~R9JKt=rQ@Ny9>X3)jNKSvgl9yDzvf=yW9aZS)s7pRmMYm%(Yk8+i&UPA*-&8s6 z1_}S7hAU1Go#ItIn%XA9dw2|T_|^#?H%VaQ8$6-IFmv|rCU<(~9KYb|miBqS^?|1x zbB0wm`*b@<=~s(*1~Qx4#IqNHZLo`Lq0VMgSGfM$e~;%;M1& literal 0 HcmV?d00001 diff --git a/mozilla/ef/gc/makefile.win b/mozilla/ef/gc/makefile.win new file mode 100644 index 00000000000..c5a5c66f438 --- /dev/null +++ b/mozilla/ef/gc/makefile.win @@ -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 diff --git a/mozilla/ef/gc/src/Makefile b/mozilla/ef/gc/src/Makefile new file mode 100644 index 00000000000..afc9ee78590 --- /dev/null +++ b/mozilla/ef/gc/src/Makefile @@ -0,0 +1,4 @@ +DEPTH = ../.. + +include manifest.mn +include $(DEPTH)/config/rules.mk diff --git a/mozilla/ef/gc/src/makefile.win b/mozilla/ef/gc/src/makefile.win new file mode 100644 index 00000000000..e53ad1924a9 --- /dev/null +++ b/mozilla/ef/gc/src/makefile.win @@ -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 diff --git a/mozilla/ef/gc/src/manifest.mn b/mozilla/ef/gc/src/manifest.mn new file mode 100644 index 00000000000..b4a4bf59f70 --- /dev/null +++ b/mozilla/ef/gc/src/manifest.mn @@ -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 + diff --git a/mozilla/ef/gc/src/smalloc.c b/mozilla/ef/gc/src/smalloc.c new file mode 100644 index 00000000000..3463b1c2e71 --- /dev/null +++ b/mozilla/ef/gc/src/smalloc.c @@ -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); +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smassert.c b/mozilla/ef/gc/src/smassert.c new file mode 100644 index 00000000000..570fa2591b8 --- /dev/null +++ b/mozilla/ef/gc/src/smassert.c @@ -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 */ + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smgc.c b/mozilla/ef/gc/src/smgc.c new file mode 100644 index 00000000000..6cbbe71fe04 --- /dev/null +++ b/mozilla/ef/gc/src/smgc.c @@ -0,0 +1,1106 @@ +/* -*- 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" +#ifdef XP_MAC +#include "prpriv.h" +#else +#include "private/prpriv.h" +#endif + +#define SM_MARK_STACK_PUSH(obj) SM_STACK_PUSH(&sm_Heap.markStack, obj) +#define SM_MARK_STACK_POP() SM_STACK_POP(&sm_Heap.markStack) + +static PRBool sm_conservOnly; + +/******************************************************************************* + * Collector + ******************************************************************************/ + +#ifdef DEBUG + +static void +SM_MARK_AND_PUSH(SMPageDesc* pd, SMBucket bucket, SMObjDesc* desc, SMObject* obj) +{ + SMPageDesc* _pd0 = (pd); + SMBucket _bucket0 = (bucket); + SMObjDesc* _desc0 = (desc); + SMObject* _obj0 = (obj); + SM_ASSERT(SM_OBJECT_CLASS(_obj0) != NULL); + if (SM_OBJECT_CLASS(_obj0) == NULL) { + int x = 0; + x += 1; + } + SM_OBJDESC_SET_MARKED(_desc0); + SM_ASSERT(SM_OBJECT_CLASS(_obj0)); + if (_bucket0 != SM_LARGE_OBJECT_BUCKET) + _pd0->allocCount++; + if (!SM_MARK_STACK_PUSH(_obj0)) { + SM_OBJDESC_SET_UNTRACED(_desc0); + } +} + +static void +SM_COPY_OBJECT(SMObject** ref, SMPageDesc* pd, SMBucket bucket, SMGenNum objGen, + SMObjDesc* desc, SMObject* obj) +{ + SMObject** _ref1 = (ref); + SMPageDesc* _pd1 = (pd); + SMBucket _bucket1 = (bucket); + SMGenNum _objGen1 = (objGen); + SMObjDesc* _desc1 = (desc); + SMObject* _obj1 = (obj); + if (_objGen1 >= SMGenNum_Senior || !SM_OBJDESC_DO_COPY(_desc1)) { + /* If the object is not yet copyable, then it hasn't lived */ + /* long enough to be copied. Just set the copyable bit, and */ + /* we'll try again next time. */ + SM_OBJDESC_SET_COPYABLE(_desc1); + SM_MARK_AND_PUSH(_pd1, _bucket1, _desc1, _obj1); + } + else if (_bucket1 == SM_LARGE_OBJECT_BUCKET) { + /* For large objects, don't copy them but just increment the */ + /* generation number for all their pages. */ + SMPageCount _count1 = _pd1->allocCount; + SM_ASSERT(!SM_OBJDESC_IS_FORWARDED(_desc1)); + SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd1)); + SM_UNROLLED_WHILE(_count1, { + SM_PAGEDESC_INCR_GEN(_pd1); + SM_ASSERT(SM_PAGEDESC_BUCKET(_pd1) == _bucket1); + SM_ASSERT(SM_PAGEDESC_GEN(_pd1) < SMGenNum_Static); + _pd1++; + }); + SM_SET_DEBUG_HEADER(SM_DECRYPT_OBJECT(_obj1), _bucket1, _objGen1 + 1); + /* We only dirty the first page of the object here because */ + /* currently the page scavenging routine processes all the */ + /* pages of a large object. We probably should make it smarter. */ + SM_MARK_AND_PUSH(_pd1, _bucket1, _desc1, _obj1); + } + else if (SM_OBJDESC_IS_FORWARDED(_desc1)) { + *_ref1 = SM_OBJECT_FORWARDING_ADDR(_obj1); + } + else { + /* Try to copy the object up too the next generation. */ + SMGenNum _newGenNum1 = (SMGenNum)((int)_objGen1 + 1); + SMGen* _newGen1 = &sm_Heap.gen[_newGenNum1]; + PRUword _allocBefore1 = _newGen1->pool.allocAmount; + SMObjStruct* _newObj1 = sm_GenPromoteObj(_newGen1, _bucket1); + if (_newObj1 == NULL) { + /* no objects on the free list -- try allocating a new page */ + _newObj1 = sm_AddGCPage(_newGenNum1, _bucket1); + } + if (_newObj1) { + /* copy the object's contents */ + SMObject* _newEObj1 = SM_ENCRYPT_OBJECT(_newObj1); + SMObjDesc* _newDesc1; + SMSmallObjSize _objSize1 = sm_ObjectSize[_bucket1]; + SMObject** _from1 = SM_OBJECT_FIELDS(_obj1); + SMObject** _to1 = SM_OBJECT_FIELDS(_newEObj1); + SM_UNROLLED_WHILE(_objSize1 >> SM_REF_BITS, + *_to1++ = *_from1++); + DBG_MEMSET(SM_DECRYPT_OBJECT(_obj1), SM_GC_FREE_PATTERN, _objSize1); + SM_SET_DEBUG_HEADER(_newObj1, _bucket1, _newGenNum1); + _newGen1->pool.allocAmount += _objSize1; + SM_ASSERT(_allocBefore1 + _objSize1 + == _newGen1->pool.allocAmount); + SM_POOL_SET_ALLOC_STATS( + &_newGen1->pool, _objSize1, + SM_ObjectSize(_newEObj1)); + SM_OBJECT_SET_FORWARDING_ADDR(_obj1, _newEObj1); + *_ref1 = _newEObj1; /* update ref */ + + /* finally, mark the new object */ + { + SMPageCount _pageNum1 = SM_PAGE_NUMBER(_newObj1); + SMSmallObjCount _pageOffset1 = + (SMSmallObjCount)SM_PAGE_OFFSET(_newObj1); + SMSmallObjCount _objNum1 = SM_DIV(_pageOffset1, _bucket1); + _pd1 = &sm_PageMgr.pageTable[_pageNum1]; + _newDesc1 = &_pd1->objTable[_objNum1]; + if (SM_OBJDESC_IS_FINALIZABLE(_desc1)) { + SM_OBJDESC_SET_FINALIZABLE(_newDesc1); + } + SM_ASSERT(SM_OBJDESC_IS_MARKED(_newDesc1)); + if (!SM_MARK_STACK_PUSH(_newEObj1)) { + SM_OBJDESC_SET_UNTRACED(_newDesc1); + } + SM_OBJDESC_SET_FORWARDED(_desc1); + } + } + else { + /* If we couldn't allocate a new copy, then just mark it. */ + SM_MARK_AND_PUSH(_pd1, _bucket1, _desc1, _obj1); + } + } +} + +#ifndef SM_NO_WRITE_BARRIER + +static void +SM_UPDATE_WRITE_BARRIER(SMGenNum _objGen2, SMCardDesc* containingCardDesc) +{ + SMCardDesc _cd2 = *(containingCardDesc); + SMGenNum _cardGen2 = SM_CARDDESC_GEN(_cd2); + if (_objGen2 < _cardGen2) { + SM_CARDDESC_SET_GEN(containingCardDesc, _objGen2); + } +} + +#else /* SM_NO_WRITE_BARRIER */ + +static void +SM_UPDATE_WRITE_BARRIER(SMGenNum _objGen2, SMCardDesc* containingCardDesc) +{ + /* no-op */ +} + +#endif /* SM_NO_WRITE_BARRIER */ + +static void +SM_SCAVENGE_OBJECT(SMObject** ref, SMGenNum genNum, PRBool copyCycle, + SMCardDesc* containingCardDesc) +{ + SMObject* _obj2 = *(ref); + SMObjStruct* _dobj2 = SM_DECRYPT_OBJECT(_obj2); + if (_dobj2) { + SMPageCount _pageNum2 = SM_PAGE_NUMBER(_dobj2); + SMPageDesc* _pd2 = &sm_PageMgr.pageTable[_pageNum2]; + SMGenNum _objGen2 = SM_PAGEDESC_GEN(_pd2); + if (copyCycle) { + SM_UPDATE_WRITE_BARRIER(_objGen2, containingCardDesc); + } + if (_objGen2 <= genNum) { + SMSmallObjCount _pageOffset2 = + (SMSmallObjCount)SM_PAGE_OFFSET(_dobj2); + SMBucket _bucket2 = SM_PAGEDESC_BUCKET(_pd2); + SMSmallObjCount _objNum2 = SM_DIV(_pageOffset2, _bucket2); + SMObjDesc* _desc2 = &_pd2->objTable[_objNum2]; + SM_ASSERT(_bucket2 != SM_LARGE_OBJECT_BUCKET + || SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd2)); + if (!SM_OBJDESC_IS_MARKED(_desc2)) { + if (copyCycle) { + SM_COPY_OBJECT(ref, _pd2, _bucket2, _objGen2, + _desc2, _obj2); + } + else { + SM_MARK_AND_PUSH(_pd2, _bucket2, _desc2, _obj2); + } + } + } + } +} + +static void +SM_SCAVENGE_RANGE(SMObject** refs, PRUword count, SMGenNum genNum, + PRBool copyCycle, SMCardDesc* containingCardDesc) +{ + SMObject** _ref3 = (refs); + PRUword _count3 = (count); + while (_count3-- > 0) { + SM_SCAVENGE_OBJECT(_ref3, genNum, copyCycle, containingCardDesc); + _ref3++; + } +} + +static void +SM_SCAVENGE_FIELDS(SMObject* obj, SMGenNum genNum, + PRBool copyCycle, SMCardDesc* objCardDesc) +{ + SMObject* _obj4 = (obj); + SMClass* _clazz4 = SM_OBJECT_CLASS(_obj4); + + SM_ASSERT(_clazz4 != NULL); + switch (SM_CLASS_KIND(_clazz4)) { + case SMClassKind_ObjectClass: { + SMFieldDesc* _fieldDesc4 = SM_CLASS_GET_INST_FIELD_DESCS(_clazz4); + PRUint16 _i4, _fdCount4 = SM_CLASS_GET_INST_FIELDS_COUNT(_clazz4); + for (_i4 = 0; _i4 < _fdCount4; _i4++) { + PRWord _offset4 = _fieldDesc4[_i4].offset; + PRUword _count4 = _fieldDesc4[_i4].count; + SMObject** _fieldRefs4 = &SM_OBJECT_FIELDS(_obj4)[_offset4]; + SM_SCAVENGE_RANGE(_fieldRefs4, _count4, genNum, + copyCycle, objCardDesc); + } + break; + } + case SMClassKind_ArrayClass: { + if (SM_IS_OBJECT_ARRAY_CLASS(_clazz4)) { + SMArray* _arr4 = (SMArray*)SM_ENSURE(SMObject, _obj4); + PRUword _size4 = SM_ARRAY_SIZE(_arr4); + SMObject** _elementRefs4 = SM_ARRAY_ELEMENTS(_arr4); + SM_SCAVENGE_RANGE(_elementRefs4, _size4, genNum, + copyCycle, objCardDesc); + } + break; + } + default: + break; + } +} + +static void +SM_DRAIN_MARK_STACK(SMGenNum genNum, PRBool copyCycle) +{ + SMObject* _obj5; + while ((_obj5 = SM_MARK_STACK_POP()) != NULL) { + SMCardDesc* _cardDesc5 = SM_OBJECT_CARDDESC(_obj5); + SM_SCAVENGE_FIELDS(_obj5, genNum, copyCycle, _cardDesc5); + } +} + +static void +SM_SCAVENGE_ALL(SMObject** ref, SMGenNum genNum, + PRBool copyCycle, SMCardDesc* containingCardDesc) +{ + SMObject** _ref6 = (ref); + SM_SCAVENGE_OBJECT(_ref6, genNum, copyCycle, containingCardDesc); + SM_DRAIN_MARK_STACK(genNum, copyCycle); +} + +static void +SM_SCAVENGE_PAGE(SMPageDesc* pd, SMGenNum genNum, + PRBool copyCycle, SMCardDesc* objCardDesc) +{ + SMSmallObjCount _j7; + SMPageDesc* _pd7 = (pd); + SMBucket _bucket7 = SM_PAGEDESC_BUCKET(_pd7); + SMSmallObjCount _objsPerPage7 = sm_ObjectsPerPage[_bucket7]; + SMSmallObjSize _objSize7 = sm_ObjectSize[_bucket7]; + SMObjStruct* _obj7 = (SMObjStruct*)SM_PAGEDESC_PAGE(_pd7); + SMObjDesc* _objTable7 = _pd7->objTable; + SM_ASSERT(_bucket7 == SM_LARGE_OBJECT_BUCKET + ? SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd7) + : 1); + for (_j7 = 0; _j7 < _objsPerPage7; _j7++) { + SMObjDesc* _od7 = &_objTable7[_j7]; + SMObject* _eobj7 = SM_ENCRYPT_OBJECT(_obj7); + if (SM_OBJDESC_IS_ALLOCATED(_od7)) { + SM_ASSERT(SM_OBJECT_CARDDESC(_eobj7) == objCardDesc); + SM_SCAVENGE_FIELDS(_eobj7, genNum, copyCycle, objCardDesc); + } + _obj7 = (SMObjStruct*)((char*)_obj7 + _objSize7); + } +} + +static void +SM_SCAVENGE_CONSERV(SMObject* obj, SMGenNum genNum, PRBool copyCycle) +{ + SMObject* _obj8 = (obj); + SMObjStruct* _dobj8 = SM_DECRYPT_OBJECT(_obj8); + + if (_dobj8 && SM_IN_HEAP(_dobj8)) { + SMPageCount _pageNum8 = SM_PAGE_NUMBER(_dobj8); + SMPageDesc* _pd8 = &sm_PageMgr.pageTable[_pageNum8]; + SMGenNum _objGen8 = SM_PAGEDESC_GEN(_pd8); + + /* Blacklist pages even outside the generation we care about. This */ + /* allows the page manager to prevent giving out blacklisted pages */ + /* unless really necessary. */ + SM_PAGEDESC_SET_BLACKLISTED(_pd8); + + if (_objGen8 <= genNum) { + SMSmallObjCount _pageOffset8 = + (SMSmallObjCount)SM_PAGE_OFFSET(_dobj8); + SMBucket _bucket8 = SM_PAGEDESC_BUCKET(_pd8); + SMSmallObjCount _objNum8 = SM_DIV(_pageOffset8, _bucket8); + SMObjDesc* _desc8 = &_pd8->objTable[_objNum8]; + if (SM_OBJDESC_IS_UNMARKED(_desc8)) { + /* If this is a conservative root, then all we can */ + /* do is pin the object, and allow it to get marked. */ + SM_ASSERT(!SM_OBJDESC_WAS_FREE(_desc8)); + if (copyCycle) + SM_OBJDESC_SET_COPYABLE_BUT_PINNED(_desc8); + else + SM_OBJDESC_SET_PINNED(_desc8); + + /* Adjust address of object so that we can find it's fields. */ + if (_bucket8 == SM_LARGE_OBJECT_BUCKET) { + if (!SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd8)) + /* go back to first page desc */ + _pd8 = SM_PAGEDESC_LARGE_OBJECT_START(_pd8); + SM_ASSERT(_pd8->allocCount != 0); + _dobj8 = (SMObjStruct*)SM_PAGEDESC_PAGE(_pd8); + } + else { + SMPage* _page8 = SM_PAGEDESC_PAGE(_pd8); + SMSmallObjSize _objSize8 = sm_ObjectSize[_bucket8]; + _dobj8 = (SMObjStruct*)((char*)_page8 + (_objSize8 * _objNum8)); + } + _obj8 = SM_ENCRYPT_OBJECT(_dobj8); + /* Finally, if the object isn't marked, mark it and push it */ + /* onto the mark stack. */ + SM_MARK_AND_PUSH(_pd8, _bucket8, _desc8, _obj8); + + /* If we ever overflow the mark stack, we'll catch it later */ + /* towards the end of the gc cycle. All that's important */ + /* now is that we pin all the conservative roots. */ + } + } + } +} + +#else /* !DEBUG */ + +xxx + +#endif /* !DEBUG */ + +/******************************************************************************/ + +SM_IMPLEMENT(void) +SM_MarkRoots(SMObject** base, PRUword count, PRBool conservative) +{ + /* Interestingly, this routine does almost all the real work to collect + the garbage -- and it's only called by the user's markRoots callback. */ + + SMGenNum genNum = sm_Heap.collectingGenNum; + PRBool copyCycle = SM_IS_COPY_CYCLE(genNum); + PRUword i = count; + + /* The user better have given us an aligned pointer. */ + SM_ASSERT(SM_IS_ALIGNED(base, SM_POINTER_ALIGNMENT)); + + /* And it better not be something that's already in the gc space. */ + SM_ASSERT(!SM_OVERLAPPING(sm_PageMgr.heapBase, + (sm_PageMgr.heapBase + sm_PageMgr.heapPageCount), + (SMPage*)base, + (SMPage*)(base + count))); + + if (conservative) { + /* Make sure we don't try conservative scavenging after we've already */ + /* started our non-conservative phase. */ + SM_ASSERT(sm_conservOnly); + + while (i-- > 0) { + SMObject* obj = *base++; + SM_SCAVENGE_CONSERV(obj, genNum, copyCycle); + } + } + else { + /* roots don't have card-table entries: */ + SMCardDesc dummy; + + /* Once we start scavenging non-conservatively, we can't go back */ + sm_conservOnly = PR_FALSE; + + /* Next drain the mark stack from any conservative objects we've + been saving up. (whew) */ + SM_DRAIN_MARK_STACK(genNum, copyCycle); + + /* Since SM_SCAVENGE_ALL is a macro, we can make two copies of it with + the copyCycle test factored out. This speeds up the inner loop + at the expence of expanding the code a bit. */ + if (copyCycle) { + while (i-- > 0) { + SM_SCAVENGE_ALL(base, genNum, PR_TRUE, &dummy); + base++; + } + } + else { + while (i-- > 0) { + SM_SCAVENGE_ALL(base, genNum, PR_FALSE, &dummy); + base++; + } + } + } +} + +static PRStatus +sm_ScanStack(PRThread* t, void** addr, PRUword count, void* closure) +{ +#if defined(XP_MAC) +#pragma unused (t, closure) +#endif + + SM_MarkRoots((SMObject**)addr, count, PR_TRUE); + return PR_SUCCESS; +} + +SM_IMPLEMENT(void) +SM_MarkThreadsConservatively(SMGenNum collectingGenNum, PRBool copyCycle, + void* closure) +{ + /* Mark the roots from the thread stacks. Do this first so + that the marked objects get their pinned bit set. */ + (void)PR_ScanStackPointers(sm_ScanStack, NULL); +} + +SM_IMPLEMENT(void) +SM_MarkThreadConservatively(PRThread* thread, SMGenNum collectingGenNum, + PRBool copyCycle, void* closure) +{ + /* Mark the roots from the thread stacks. Do this first so + that the marked objects get their pinned bit set. */ + (void)PR_ThreadScanStackPointers(thread, sm_ScanStack, NULL); +} + +/******************************************************************************/ + +#if !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG) + +static void +sm_CheckObject(SMObject* obj, SMGenNum cardGen) +{ + if (obj) { + SMObjStruct* dobj = SM_DECRYPT_OBJECT(obj); + SMPageDesc* pd = SM_OBJECT_PAGEDESC(dobj); + SMObjDesc* od = SM_OBJECT_HEADER_FROM_PAGEDESC(dobj, pd); + SMGenNum objGen = SM_PAGEDESC_GEN(pd); + SM_ASSERT(cardGen <= objGen); + SM_ASSERT(SM_OBJDESC_IS_MARKED(od)); + } +} + +static void +sm_CheckRange(SMObject** refs, PRUword count, SMGenNum cardGen) +{ + SMObject** ref = refs; + PRUword i = count; + while (i-- > 0) { + sm_CheckObject(*ref, cardGen); + ref++; + } +} + +static void +sm_CheckObjectFields(SMObject* obj, SMGenNum cardGen) +{ + SMClass* clazz = SM_OBJECT_CLASS(obj); + SM_ASSERT(clazz != NULL); + 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_CheckRange(fieldRefs, count, cardGen); + } + break; + } + case SMClassKind_ArrayClass: { + if (SM_IS_OBJECT_ARRAY_CLASS(clazz)) { + SMArray* arr = (SMArray*)obj; + PRUword size = SM_ARRAY_SIZE(arr); + SMObject** elementRefs = SM_ARRAY_ELEMENTS(arr); + sm_CheckRange(elementRefs, size, cardGen); + } + break; + } + default: + break; + } +} + +static void +sm_CheckWriteBarrier(SMGenNum collectingGenNum) +{ + SMPageCount i; + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + if (collectingGenNum < pdGen && SM_IS_GC_SPACE(pdGen)) { + SMSmallObjCount j; + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + if (!(bucket == SM_LARGE_OBJECT_BUCKET + && !SM_PAGEDESC_IS_LARGE_OBJECT_START(pd))) { + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMSmallObjSize objSize = sm_ObjectSize[bucket]; + SMObjStruct* dobj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + SMObject* obj = SM_ENCRYPT_OBJECT(dobj); + SMObjDesc* objTable = pd->objTable; + SMCardDesc* cardDesc = SM_OBJECT_CARDDESC(obj); + SMGenNum cardGen = SM_CARDDESC_GEN(*cardDesc); + + /* Card should have been processed */ + SM_ASSERT(!SM_CARDDESC_IS_DIRTY(*cardDesc)); + + /* Make sure all the objects on the page refer to objects + that have been marked */ + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (SM_OBJDESC_IS_ALLOCATED(od)) { + sm_CheckObjectFields(obj, cardGen); + } + dobj = (SMObjStruct*)((char*)dobj + objSize); + obj = SM_ENCRYPT_OBJECT(dobj); + } + } + } + } +} + +#define SM_CHECK_WRITE_BARRIER(genNum) sm_CheckWriteBarrier(genNum) + +#else /* !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG) */ + +#define SM_CHECK_WRITE_BARRIER(genNum) /* no-op */ + +#endif /* !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG) */ + +/******************************************************************************/ + +static void +sm_FinishMarking(SMGenNum collectingGenNum, + SMGenNum overflowGenNum, PRBool copyCycle) +{ + SMPageCount i; + SMSmallObjCount j; + + /* If we ever overflowed the mark stack during the marking process, + look for untraced objects and mark them too. */ + do { + if (sm_Heap.markStack.overflowCount > 0) { + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + if (pdGen <= overflowGenNum) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMSmallObjSize objSize = sm_ObjectSize[bucket]; + SMObjDesc* objTable = pd->objTable; + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + SMObject* eobj = SM_ENCRYPT_OBJECT(obj); + SMCardDesc* cd = SM_OBJECT_CARDDESC(eobj); + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (SM_OBJDESC_IS_UNTRACED(od)) { + eobj = SM_ENCRYPT_OBJECT(obj); + SM_OBJDESC_CLEAR_UNTRACED(od); + SM_SCAVENGE_FIELDS(eobj, collectingGenNum, copyCycle, cd); + if (--sm_Heap.markStack.overflowCount == 0) { + goto finish; /* XXX should assert that there aren't more */ + } + } + obj = (SMObjStruct*)((char*)obj + objSize); + } + } + } + } + finish: + SM_DRAIN_MARK_STACK(collectingGenNum, copyCycle); + } while (sm_Heap.markStack.overflowCount > 0); + SM_ASSERT(SM_STACK_IS_EMPTY(&sm_Heap.markStack)); +} + +void +sm_Collect0(SMGenNum collectingGenNum, PRBool copyCycle) +{ + SMPageCount i; + SMSmallObjCount j; + SMGenNum g, overflowGenNum; + + overflowGenNum = copyCycle && collectingGenNum < SMGenNum_Senior + ? (SMGenNum)((int)collectingGenNum + 1) + : collectingGenNum; + + /************************************************************************** + * 1. Enter monitors that we need to hold during gc + *************************************************************************/ + + if (sm_Heap.beforeGCHook) + sm_Heap.beforeGCHook(collectingGenNum, + sm_Heap.gen[SMGenNum_Freshman].collectCount, + copyCycle, sm_Heap.beforeGCClosure); + PR_EnterMonitor(sm_Heap.finalizerMon); + PR_EnterMonitor(sm_PageMgr.monitor); + sm_Heap.collectingGenNum = collectingGenNum; /* set after being in the finalizerMon */ + sm_PageMgr.alreadyLocked = PR_TRUE; /* set after locking */ + PR_SuspendAll(); + sm_conservOnly = PR_TRUE; + + /************************************************************************** + * 2. Reset alloc counts and clear mark bits + *************************************************************************/ + + for (g = SMGenNum_Freshman; g <= collectingGenNum; g++) { + SMGen* gen = &sm_Heap.gen[g]; + SMBucket b; + + /* Increment the collect count for all generations we've looked at. This + allows us to figure out when each generation needs a copy cycle. */ + gen->collectCount++; + gen->pool.allocAmount = 0; + + /* Finish sweeping and set the state of the previously unmarked objects + to freed. That way we can distinguish a free object from one we simply + haven't marked yet. */ + for (b = 0; b < SM_LARGE_OBJECT_BUCKET; b++) { + SMSweepList* sweepList = &gen->pool.sweepList[b]; +#if 0 + SMPageDesc* pd = sweepList->sweepPage; + SMObjDesc* objTable = pd->objTable; + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[b]; + for (j = sweepList->sweepIndex; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (!SM_OBJDESC_IS_MARKED(od)) { + SM_OBJDESC_SET_FREE(od); + } + } +#endif + /* Reset the sweep lists -- we'll rebuild them below */ + sweepList->sweepPage = NULL; + sweepList->sweepIndex = 0; + } + } + + /* first clear all the mark bits */ + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pageGenNum = SM_PAGEDESC_GEN(pd); + SM_PAGEDESC_CLEAR_BLACKLISTED(pd); + if (pageGenNum <= collectingGenNum) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMObjDesc* objTable = pd->objTable; + SMGen* gen = &sm_Heap.gen[pageGenNum]; + SMSweepList* sweepList = &gen->pool.sweepList[bucket]; + + SM_ASSERT(SM_IS_GC_SPACE(pageGenNum)); + + /* XXX We can eliminate this loop by swapping the marked and + unmarked colors. */ + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + SM_OBJDESC_SET_UNMARKED(od); + } + + if (bucket != SM_LARGE_OBJECT_BUCKET) { + pd->allocCount = 0; + + /* Thread all pages onto the sweep list. During sweeping + we'll free up any completely empty pages. */ + pd->next = sweepList->sweepPage; + sweepList->sweepPage = pd; + } + } + } + + /************************************************************************** + * 3. Mark roots + *************************************************************************/ + + if (sm_Heap.markRootsProc) + sm_Heap.markRootsProc(collectingGenNum, copyCycle, sm_Heap.markRootsClosure); + + sm_FinishMarking(collectingGenNum, overflowGenNum, copyCycle); + + /************************************************************************** + * 4. Process the write-barrier + *************************************************************************/ + +#ifdef SM_NO_WRITE_BARRIER + /* No write barrier -- so check all senior pages. */ + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + if (collectingGenNum < pdGen && SM_IS_GC_SPACE(pdGen)) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + if (bucket == SM_LARGE_OBJECT_BUCKET) { + SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(pd)); + i += (pd->allocCount - 1); /* skip over the rest of the large obj */ + } + SM_SCAVENGE_PAGE(pd, collectingGenNum, copyCycle, NULL); + } + } +#else /* !SM_NO_WRITE_BARRIER */ + /* Mark the roots from the dirty pages. */ + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMCardDesc* cardDesc = &sm_Heap.cardTableMem[i]; + PRInt8 cardDescGen = SM_CARDDESC_GEN(*cardDesc); /* must be signed for comparison */ + if (cardDescGen <= collectingGenNum) { /* card references gen that we're collecting */ + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; /* page of this card */ + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + SMPage* page = SM_PAGEDESC_PAGE(pd); + SMPageCount pageNum = SM_PAGE_NUMBER(page); + + SM_ASSERT(&SM_CardTable[pageNum] == cardDesc); + + /* First set the min gen for this card to be the card's gen. This + indicates that we've examined this card, and it's no longer dirty. */ + if (SM_CARDDESC_IS_DIRTY(*cardDesc)) { + SM_CARDDESC_SET_GEN(cardDesc, pdGen); + } + + /* Then scavenge the objects on the card while looking for an even + younger generation to which the card refers. */ + if (collectingGenNum < pdGen && SM_IS_GC_SPACE(pdGen)) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + if (bucket == SM_LARGE_OBJECT_BUCKET) { + SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(pd)); + i += (pd->allocCount - 1); /* skip over the rest of the large obj */ + } + SM_SCAVENGE_PAGE(pd, collectingGenNum, copyCycle, cardDesc); + } + } + } +#endif /* !SM_NO_WRITE_BARRIER */ + + sm_FinishMarking(collectingGenNum, overflowGenNum, copyCycle); + + /* Check that we haven't missed any pages that should have been dirty. */ + SM_CHECK_WRITE_BARRIER(collectingGenNum); + + /************************************************************************** + * 5. Process finalizable objects + *************************************************************************/ + + /* Look for any finalizable objects and mark them as needing finalization. */ + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + if (pdGen <= collectingGenNum) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + if (bucket == SM_LARGE_OBJECT_BUCKET) { + SMObjDesc* od = &pd->objTable[0]; + if (!SM_OBJDESC_IS_ALLOCATED(od) && SM_OBJDESC_IS_FINALIZABLE(od)) { + SM_OBJDESC_SET_NEEDS_FINALIZATION(od); + } + i += (pd->allocCount - 1); /* skip over the rest of the large obj */ + } + else { + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMSmallObjSize objSize = sm_ObjectSize[bucket]; + SMObjDesc* objTable = pd->objTable; + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (!SM_OBJDESC_IS_ALLOCATED(od) && SM_OBJDESC_IS_FINALIZABLE(od)) { + SM_OBJDESC_SET_NEEDS_FINALIZATION(od); + } + } + } + } + } + /* Then mark any objects that are going to have their finalizers run so that + they're kept alive until the finalizers have finished. */ + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + if (pdGen <= collectingGenNum) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + if (bucket == SM_LARGE_OBJECT_BUCKET) { + SMObjDesc* od = &pd->objTable[0]; + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + if (!SM_OBJDESC_IS_ALLOCATED(od) && SM_OBJDESC_IS_FINALIZABLE(od)) { + SMObject* eobj = SM_ENCRYPT_OBJECT(obj); + SMCardDesc dummy; + /* Scavenge the object, but don't allow it to be copied. + It's going to get collected soon anyway. */ + SM_SCAVENGE_ALL(&eobj, collectingGenNum, PR_FALSE, &dummy); + } + i += (pd->allocCount - 1); /* skip over the rest of the large obj */ + } + else { + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMSmallObjSize objSize = sm_ObjectSize[bucket]; + SMObjDesc* objTable = pd->objTable; + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (!SM_OBJDESC_IS_ALLOCATED(od) && SM_OBJDESC_IS_FINALIZABLE(od)) { + SMObject* eobj = SM_ENCRYPT_OBJECT(obj); + SMCardDesc dummy; + /* Scavenge the object, but don't allow it to be copied. + It's going to get collected soon anyway. */ + SM_SCAVENGE_ALL(&eobj, collectingGenNum, PR_FALSE, &dummy); + } + obj = (SMObjStruct*)((char*)obj + objSize); + } + } + } + } + + /* We need to again check for mark stack overflow and drain the mark stack + due to marking finalizable objects. */ + sm_FinishMarking(collectingGenNum, overflowGenNum, copyCycle); + + /************************************************************************** + * 6. Return free pages to the page manager + *************************************************************************/ + + for (g = SMGenNum_Freshman; g <= collectingGenNum; g++) { + SMGen* gen = &sm_Heap.gen[g]; + SMPool* pool = &gen->pool; + SMBucket b; + SMSweepList* sweepList; + SMPageDesc** pdAddr; + SMPageDesc* pd; + /* Small object pages */ + for (b = 0; b < SM_LARGE_OBJECT_BUCKET; b++) { + sweepList = &pool->sweepList[b]; + pdAddr = &sweepList->sweepPage; + while ((pd = *pdAddr) != NULL) { + if (pd->allocCount == 0) { + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + *pdAddr = pd->next; /* unlink the page */ + SM_GEN_DESTROY_PAGE(gen, pd); + } + else { + pdAddr = &pd->next; + } + } + } + /* Large object pages */ + sweepList = &pool->sweepList[SM_LARGE_OBJECT_BUCKET]; + pdAddr = &sweepList->sweepPage; + while ((pd = *pdAddr) != NULL) { + if (!SM_OBJDESC_IS_ALLOCATED(&pd->objTable[0])) { + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(pd)); + *pdAddr = pd->next; /* unlink the large object */ + SM_GEN_FREE_LARGE_OBJ(gen, obj); + } + else { + pdAddr = &pd->next; + } + } + } +#ifdef DEBUG + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pdGen = SM_PAGEDESC_GEN(pd); + if (pdGen <= collectingGenNum) { + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + if (bucket == SM_LARGE_OBJECT_BUCKET) { + SMObjDesc* od = &pd->objTable[0]; + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + PRUword objSize = pd->allocCount * SM_PAGE_SIZE; + if (!SM_OBJDESC_IS_ALLOCATED(od)) { + DBG_MEMSET(obj, SM_GC_FREE_PATTERN, objSize); + } + i += (pd->allocCount - 1); /* skip over the rest of the large obj */ + } + else { + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMSmallObjSize objSize = sm_ObjectSize[bucket]; + SMObjDesc* objTable = pd->objTable; + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (!SM_OBJDESC_IS_ALLOCATED(od)) { + DBG_MEMSET(obj, SM_GC_FREE_PATTERN, objSize); + } + obj = (SMObjStruct*)((char*)obj + objSize); + } + } + } + } +#endif + SM_ASSERT(SM_STACK_IS_EMPTY(&sm_Heap.markStack)); + SM_VERIFY_HEAP(); + + /************************************************************************** + * 7. Release monitors + *************************************************************************/ + + PR_ResumeAll(); + sm_PageMgr.alreadyLocked = PR_FALSE; /* set before unlocking */ + PR_ExitMonitor(sm_PageMgr.monitor); + PR_Notify(sm_Heap.finalizerMon); + PR_ExitMonitor(sm_Heap.finalizerMon); + if (sm_Heap.afterGCHook) + sm_Heap.afterGCHook(collectingGenNum, + sm_Heap.gen[SMGenNum_Freshman].collectCount, + copyCycle, sm_Heap.afterGCClosure); +} + +SM_IMPLEMENT(void) +SM_Collect(void) +{ + sm_Collect0(SMGenNum_Senior, SM_IS_COPY_CYCLE(SMGenNum_Senior)); +} + +/******************************************************************************* + * Finalizer + ******************************************************************************/ + +static PRBool sm_didFinalizationWork = PR_FALSE; + +static void PR_CALLBACK +sm_FinalizerLoop(void* unused) +{ +#ifdef XP_MAC +#pragma unused (unused) +#endif + + PR_EnterMonitor(sm_Heap.finalizerMon); + while (PR_TRUE) { + SMPageCount i; + SMGenNum genNum; + + PR_Notify(sm_Heap.finalizerMon); /* ready */ + if (!sm_Heap.keepRunning) + break; + + /* wait to be told to finalize */ + PR_Wait(sm_Heap.finalizerMon, PR_INTERVAL_NO_TIMEOUT); + + /* set this variable back to false after we've allowed + our notifier to read it (during PR_Wait) */ + sm_didFinalizationWork = PR_FALSE; + +// SM_VERIFY_HEAP(); + + genNum = sm_Heap.collectingGenNum; + if (genNum >= SMGenNum_Static) + continue; /* nothing to do yet */ + + for (i = 0; i < sm_PageMgr.heapPageCount; i++) { + SMPageDesc* pd = &sm_PageMgr.pageTableMem[i]; + SMGenNum pageGenNum = SM_PAGEDESC_GEN(pd); + if (pageGenNum <= genNum) { + /* found a page belonging to the generation we just collected */ + SMSmallObjCount j; + SMBucket bucket = SM_PAGEDESC_BUCKET(pd); + SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket]; + SMSmallObjSize objSize = sm_ObjectSize[bucket]; + SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd); + SMObjDesc* objTable = pd->objTable; + for (j = 0; j < objsPerPage; j++) { + SMObjDesc* od = &objTable[j]; + if (SM_OBJDESC_NEEDS_FINALIZATION(od)) { + SMObject* eobj = SM_ENCRYPT_OBJECT(obj); + SMClass* clazz = SM_OBJECT_CLASS(eobj); + SMFinalizeFun finalize = SM_CLASS_FINALIZER(clazz); + PR_ExitMonitor(sm_Heap.finalizerMon); + + /* run the finalizer */ + finalize(eobj); + + PR_EnterMonitor(sm_Heap.finalizerMon); + SM_OBJDESC_CLEAR_FINALIZATION(od); + sm_didFinalizationWork = PR_TRUE; + } + obj = (SMObjStruct*)((char*)obj + objSize); + } + } + } + sm_Heap.collectingGenNum = SMGenNum_Free; +// SM_VERIFY_HEAP(); + } + PR_ExitMonitor(sm_Heap.finalizerMon); +} + +static PRBool +sm_ForceFinalization(void) +{ + PRBool didFinalizationWork; + PR_EnterMonitor(sm_Heap.finalizerMon); + PR_Notify(sm_Heap.finalizerMon); + PR_Wait(sm_Heap.finalizerMon, PR_INTERVAL_NO_TIMEOUT); + didFinalizationWork = sm_didFinalizationWork; + PR_ExitMonitor(sm_Heap.finalizerMon); + return didFinalizationWork; +} + +SM_IMPLEMENT(void) +SM_RunFinalization(void) +{ + (void)sm_ForceFinalization(); +} + +PRStatus +sm_InitFinalizer(void) +{ + sm_Heap.finalizerMon = PR_NewMonitor(); + if (sm_Heap.finalizerMon == NULL) + return PR_FAILURE; + /* set keepRunning before creating the finalizer loop */ + sm_Heap.keepRunning = PR_TRUE; + PR_EnterMonitor(sm_Heap.finalizerMon); + sm_Heap.finalizer = PR_CreateThread(PR_SYSTEM_THREAD, + sm_FinalizerLoop, NULL, + PR_PRIORITY_LOW, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (sm_Heap.finalizer == NULL) { + PR_ExitMonitor(sm_Heap.finalizerMon); + PR_DestroyMonitor(sm_Heap.finalizerMon); + return PR_FAILURE; + } + /* Wait for the finalizer loop to be ready. */ + PR_Wait(sm_Heap.finalizerMon, PR_INTERVAL_NO_TIMEOUT); + PR_ExitMonitor(sm_Heap.finalizerMon); + return PR_SUCCESS; +} + +PRBool +sm_CollectAndFinalizeAll(PRUword spaceNeeded) +{ + PRBool doingFinalizationWork, doCopy = PR_TRUE; + static PRUword lastCollectCount = 0; + + if (lastCollectCount == sm_Heap.gen[SMGenNum_Freshman].collectCount) { + /* Heuristic: Returning false from this routine causes + allocation to fail -- all the way up to the user program. + This gives the user the opportunity to free up some stuff + (hopefully), so if we come through here again and we haven't + collected yet for other reasons, we don't want to fail again + without trying to collect and finalize again. So reset the + lastCollectCount so that we don't continuously fail. */ + lastCollectCount = 0; + return PR_FALSE; + } + + PR_EnterMonitor(sm_Heap.finalizerMon); + + do { + /* force copying to free space in gen 0 */ + sm_Collect0(SMGenNum_Senior, doCopy); + doingFinalizationWork = sm_ForceFinalization(); + doCopy = PR_FALSE; /* only copy the first time */ + } while (doingFinalizationWork); + + lastCollectCount = sm_Heap.gen[SMGenNum_Freshman].collectCount; + + PR_ExitMonitor(sm_Heap.finalizerMon); + return PR_TRUE; +} + +PRStatus +sm_FiniFinalizer(PRBool finalizeOnExit) +{ + if (finalizeOnExit) { + (void)sm_CollectAndFinalizeAll(0); + } + + /* shut down the finalizer thread */ + sm_Heap.keepRunning = PR_FALSE; + (void)sm_ForceFinalization(); /* one last time */ + + /* and wait for it to finish */ + PR_JoinThread(sm_Heap.finalizer); + + PR_DestroyMonitor(sm_Heap.finalizerMon); + return PR_SUCCESS; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smgen.c b/mozilla/ef/gc/src/smgen.c new file mode 100644 index 00000000000..1ea147f9359 --- /dev/null +++ b/mozilla/ef/gc/src/smgen.c @@ -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 + +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 */ + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smhash.c b/mozilla/ef/gc/src/smhash.c new file mode 100644 index 00000000000..0d2db971fb6 --- /dev/null +++ b/mozilla/ef/gc/src/smhash.c @@ -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; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smheap.c b/mozilla/ef/gc/src/smheap.c new file mode 100644 index 00000000000..24acd854a9a --- /dev/null +++ b/mozilla/ef/gc/src/smheap.c @@ -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 + +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 */ + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smobj.c b/mozilla/ef/gc/src/smobj.c new file mode 100644 index 00000000000..d9e8c82c67c --- /dev/null +++ b/mozilla/ef/gc/src/smobj.c @@ -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); +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smpage.c b/mozilla/ef/gc/src/smpage.c new file mode 100644 index 00000000000..ce757dee7fe --- /dev/null +++ b/mozilla/ef/gc/src/smpage.c @@ -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 +#elif defined(XP_MAC) +#include +#include +#include +#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 + +#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); +} + +/******************************************************************************/ + diff --git a/mozilla/ef/gc/src/smpool.c b/mozilla/ef/gc/src/smpool.c new file mode 100644 index 00000000000..6e51acbbc15 --- /dev/null +++ b/mozilla/ef/gc/src/smpool.c @@ -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 + +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 */ + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smstack.c b/mozilla/ef/gc/src/smstack.c new file mode 100644 index 00000000000..c8e680c9e86 --- /dev/null +++ b/mozilla/ef/gc/src/smstack.c @@ -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); +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/src/smtrav.c b/mozilla/ef/gc/src/smtrav.c new file mode 100644 index 00000000000..c044022deb8 --- /dev/null +++ b/mozilla/ef/gc/src/smtrav.c @@ -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) */ + +/******************************************************************************/ diff --git a/mozilla/ef/gc/test/Makefile b/mozilla/ef/gc/test/Makefile new file mode 100644 index 00000000000..afc9ee78590 --- /dev/null +++ b/mozilla/ef/gc/test/Makefile @@ -0,0 +1,4 @@ +DEPTH = ../.. + +include manifest.mn +include $(DEPTH)/config/rules.mk diff --git a/mozilla/ef/gc/test/alloc.c b/mozilla/ef/gc/test/alloc.c new file mode 100644 index 00000000000..d0b5af745c1 --- /dev/null +++ b/mozilla/ef/gc/test/alloc.c @@ -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 + +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); + } +} diff --git a/mozilla/ef/gc/test/buckets.c b/mozilla/ef/gc/test/buckets.c new file mode 100644 index 00000000000..52ffbe8087c --- /dev/null +++ b/mozilla/ef/gc/test/buckets.c @@ -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 + +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); +} diff --git a/mozilla/ef/gc/test/divtest.c b/mozilla/ef/gc/test/divtest.c new file mode 100644 index 00000000000..842f76b19d3 --- /dev/null +++ b/mozilla/ef/gc/test/divtest.c @@ -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 +#include + +#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 +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/test/hashtable.cpp b/mozilla/ef/gc/test/hashtable.cpp new file mode 100644 index 00000000000..ebee7352a62 --- /dev/null +++ b/mozilla/ef/gc/test/hashtable.cpp @@ -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 +#include +#include + +#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 { +public: + EFHash(Pool& pool) : FastHashTable(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; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/test/hashtest.c b/mozilla/ef/gc/test/hashtest.c new file mode 100644 index 00000000000..71ac791720e --- /dev/null +++ b/mozilla/ef/gc/test/hashtest.c @@ -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 +#include +#include + +#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; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/test/makefile.win b/mozilla/ef/gc/test/makefile.win new file mode 100644 index 00000000000..38feb8dc67d --- /dev/null +++ b/mozilla/ef/gc/test/makefile.win @@ -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 diff --git a/mozilla/ef/gc/test/maltest.c b/mozilla/ef/gc/test/maltest.c new file mode 100644 index 00000000000..14392ae5d48 --- /dev/null +++ b/mozilla/ef/gc/test/maltest.c @@ -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 +#include + +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; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/test/manifest.mn b/mozilla/ef/gc/test/manifest.mn new file mode 100644 index 00000000000..e91a4e5f95b --- /dev/null +++ b/mozilla/ef/gc/test/manifest.mn @@ -0,0 +1,10 @@ +MODULE = sm + +DEPTH = ../.. + +CSRCS = \ + maltest.c \ + $(NULL) + +REQUIRES = sm nspr20 + diff --git a/mozilla/ef/gc/test/msgctest.c b/mozilla/ef/gc/test/msgctest.c new file mode 100644 index 00000000000..3e91951ef97 --- /dev/null +++ b/mozilla/ef/gc/test/msgctest.c @@ -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 +#include + +#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<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; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/test/pagetest.c b/mozilla/ef/gc/test/pagetest.c new file mode 100644 index 00000000000..cf676ab7206 --- /dev/null +++ b/mozilla/ef/gc/test/pagetest.c @@ -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 +#include + +#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; +} diff --git a/mozilla/ef/gc/test/smtest.c b/mozilla/ef/gc/test/smtest.c new file mode 100644 index 00000000000..d9634784212 --- /dev/null +++ b/mozilla/ef/gc/test/smtest.c @@ -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 +#include + +#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; +} + +/******************************************************************************/ diff --git a/mozilla/ef/gc/todo.txt b/mozilla/ef/gc/todo.txt new file mode 100644 index 00000000000..8371b390035 --- /dev/null +++ b/mozilla/ef/gc/todo.txt @@ -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 +