summaryrefslogtreecommitdiff
path: root/lib/msgc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/msgc')
-rw-r--r--lib/msgc/.cvsignore1
-rw-r--r--lib/msgc/Makefile.in52
-rw-r--r--lib/msgc/include/.cvsignore1
-rw-r--r--lib/msgc/include/Makefile.in57
-rw-r--r--lib/msgc/include/gcint.h129
-rw-r--r--lib/msgc/include/prgc.h419
-rw-r--r--lib/msgc/src/.cvsignore1
-rw-r--r--lib/msgc/src/Makefile.in93
-rw-r--r--lib/msgc/src/os2gc.c83
-rw-r--r--lib/msgc/src/prgcapi.c330
-rw-r--r--lib/msgc/src/prmsgc.c3320
-rw-r--r--lib/msgc/src/unixgc.c155
-rw-r--r--lib/msgc/src/win32gc.c129
-rw-r--r--lib/msgc/tests/.cvsignore1
-rw-r--r--lib/msgc/tests/Makefile.in270
-rw-r--r--lib/msgc/tests/gc1.c242
-rw-r--r--lib/msgc/tests/thrashgc.c242
17 files changed, 5525 insertions, 0 deletions
diff --git a/lib/msgc/.cvsignore b/lib/msgc/.cvsignore
new file mode 100644
index 0000000..f3c7a7c
--- /dev/null
+++ b/lib/msgc/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/lib/msgc/Makefile.in b/lib/msgc/Makefile.in
new file mode 100644
index 0000000..d7299b2
--- /dev/null
+++ b/lib/msgc/Makefile.in
@@ -0,0 +1,52 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape Portable Runtime (NSPR).
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+MOD_DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+DIRS = include src tests
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/lib/msgc/include/.cvsignore b/lib/msgc/include/.cvsignore
new file mode 100644
index 0000000..f3c7a7c
--- /dev/null
+++ b/lib/msgc/include/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/lib/msgc/include/Makefile.in b/lib/msgc/include/Makefile.in
new file mode 100644
index 0000000..86e152a
--- /dev/null
+++ b/lib/msgc/include/Makefile.in
@@ -0,0 +1,57 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape Portable Runtime (NSPR).
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/config.mk
+
+EXPORT_HEADERS = prgc.h
+HEADERS = $(EXPORT_HEADERS) gcint.h
+
+RELEASE_HEADERS = $(EXPORT_HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(EXPORT_HEADERS)
+ $(INSTALL) -m 444 $(EXPORT_HEADERS) $(dist_includedir)
diff --git a/lib/msgc/include/gcint.h b/lib/msgc/include/gcint.h
new file mode 100644
index 0000000..10048f0
--- /dev/null
+++ b/lib/msgc/include/gcint.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef gcint_h___
+#define gcint_h___
+
+#include "prmon.h"
+#include "prgc.h"
+
+extern PRLogModuleInfo *_pr_msgc_lm;
+extern GCInfo _pr_gcData;
+
+#if defined(_WIN32) && !defined(DEBUG)
+#undef INLINE_LOCK
+#endif
+
+#ifdef INLINE_LOCK
+#define LOCK_GC() EnterCriticalSection(&_pr_gcData.lock->mutexHandle)
+#define UNLOCK_GC() LeaveCriticalSection(&_pr_gcData.lock->mutexHandle)
+#else
+#define LOCK_GC() PR_EnterMonitor(_pr_gcData.lock)
+#define UNLOCK_GC() PR_ExitMonitor (_pr_gcData.lock)
+#define GC_IS_LOCKED() (PR_GetMonitorEntryCount(_pr_gcData.lock)!=0)
+#endif
+
+#ifdef DEBUG
+#define _GCTRACE(x, y) if (_pr_gcData.flags & x) GCTrace y
+#else
+#define _GCTRACE(x, y)
+#endif
+
+extern GCBeginGCHook *_pr_beginGCHook;
+extern void *_pr_beginGCHookArg;
+extern GCBeginGCHook *_pr_endGCHook;
+extern void *_pr_endGCHookArg;
+
+extern GCBeginFinalizeHook *_pr_beginFinalizeHook;
+extern void *_pr_beginFinalizeHookArg;
+extern GCBeginFinalizeHook *_pr_endFinalizeHook;
+extern void *_pr_endFinalizeHookArg;
+
+extern int _pr_do_a_dump;
+extern FILE *_pr_dump_file;
+
+extern PRLogModuleInfo *_pr_gc_lm;
+
+/*
+** Root finders. Root finders are used by the GC to find pointers into
+** the GC heap that are not contained in the GC heap.
+*/
+typedef struct RootFinderStr RootFinder;
+
+struct RootFinderStr {
+ RootFinder *next;
+ GCRootFinder *func;
+ char *name;
+ void *arg;
+};
+extern RootFinder *_pr_rootFinders;
+
+typedef struct CollectorTypeStr {
+ GCType gctype;
+ PRUint32 flags;
+} CollectorType;
+
+#define GC_MAX_TYPES 256
+extern CollectorType *_pr_collectorTypes;
+
+#define _GC_TYPE_BUSY 0x1
+#define _GC_TYPE_FINAL 0x2
+#define _GC_TYPE_WEAK 0x4
+
+/* Slot in _pr_gcTypes used for free memory */
+#define FREE_MEMORY_TYPEIX 255
+
+extern void _PR_InitGC(PRWord flags);
+extern void _MD_InitGC(void);
+extern void PR_CALLBACK _PR_ScanFinalQueue(void *notused);
+
+/*
+** Grow the GC Heap.
+*/
+extern void *_MD_GrowGCHeap(PRUint32 *sizep);
+
+/*
+** Extend the GC Heap.
+*/
+extern PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize);
+
+/*
+** Free a GC segment.
+*/
+extern void _MD_FreeGCSegment(void *base, PRInt32 len);
+
+#endif /* gcint_h___ */
diff --git a/lib/msgc/include/prgc.h b/lib/msgc/include/prgc.h
new file mode 100644
index 0000000..859f7b1
--- /dev/null
+++ b/lib/msgc/include/prgc.h
@@ -0,0 +1,419 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef prgc_h___
+#define prgc_h___
+
+/*
+** API to NSPR gc memory system.
+*/
+#include "prtypes.h"
+#include "prmon.h"
+#include "prthread.h"
+#include <stdio.h>
+
+#if defined(WIN16)
+#define GCPTR __far
+#else
+#define GCPTR
+#endif
+
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Initialize the garbage collector.
+** "flags" is the trace flags (see below).
+** "initialHeapSize" is the initial size of the heap and may be zero
+** if the default is desired.
+** "segmentSize" is the size of each segment of memory added to the
+** heap when the heap is grown.
+*/
+PR_EXTERN(void) PR_InitGC(
+ PRWord flags, PRInt32 initialHeapSize, PRInt32 segmentSize, PRThreadScope scope);
+
+/*
+** Shuts down gc and frees up all memory associated with it.
+*/
+PR_EXTERN(void) PR_ShutdownGC(PRBool finalizeOnExit);
+
+/*
+** This walk function will be called for every gc object in the
+** heap as it is walked. If it returns non-zero, the walk is terminated.
+*/
+typedef PRInt32 (*PRWalkFun)(void GCPTR* obj, void* data);
+
+/*
+** GC Type record. This defines all of the GC operations used on a
+** particular object type. These structures are passed to
+** PR_RegisterType.
+*/
+typedef struct GCType {
+ /*
+ ** Scan an object that is in the GC heap and call GCInfo.livePointer
+ ** on all of the pointers in it. If this slot is null then the object
+ ** won't be scanned (i.e. it has no embedded pointers).
+ */
+ void (PR_CALLBACK *scan)(void GCPTR *obj);
+
+ /*
+ ** Finalize an object that has no references. This is called by the
+ ** GC after it has determined where the object debris is but before
+ ** it has moved the debris to the logical "free list". The object is
+ ** marked alive for this call and removed from the list of objects
+ ** that need finalization (finalization only happens once for an
+ ** object). If this slot is null then the object doesn't need
+ ** finalization.
+ */
+ void (PR_CALLBACK *finalize)(void GCPTR *obj);
+
+ /*
+ ** Dump out an object during a PR_DumpGCHeap(). This is used as a
+ ** debugging tool.
+ */
+ void (PR_CALLBACK *dump)(FILE *out, void GCPTR *obj, PRBool detailed, PRIntn indentLevel);
+
+ /*
+ ** Add object to summary table.
+ */
+ void (PR_CALLBACK *summarize)(void GCPTR *obj, PRUint32 bytes);
+
+ /*
+ ** Free hook called by GC when the object is being freed.
+ */
+ void (PR_CALLBACK *free)(void *obj);
+
+ /* Weak pointer support: If the object has a weak pointer (Note:
+ at most one), this function is used to get the weak link's
+ offset from the start of the body of a gc object */
+ PRUint32 (PR_CALLBACK *getWeakLinkOffset)(void *obj);
+
+ /* Descriptive character for dumping this GCType */
+ char kindChar;
+
+ /*
+ ** Walker routine. This routine should apply fun(obj->ptr, data)
+ ** for every gc pointer within the object.
+ */
+ PRInt32 (PR_CALLBACK *walk)(void GCPTR *obj, PRWalkFun fun, void* data);
+} GCType;
+
+/*
+** This data structure must be added as the hash table passed to
+** the summarize method of GCType.
+*/
+typedef struct PRSummaryEntry {
+ void* clazz;
+ PRInt32 instancesCount;
+ PRInt32 totalSize;
+} PRSummaryEntry;
+
+/*
+** This function pointer must be registered by users of nspr
+** to produce the finally summary after all object in the
+** heap have been visited.
+*/
+typedef void (PR_CALLBACK *PRSummaryPrinter)(FILE *out, void* closure);
+
+PR_EXTERN(void) PR_CALLBACK PR_RegisterSummaryPrinter(PRSummaryPrinter fun, void* closure);
+
+typedef void PR_CALLBACK GCRootFinder(void *arg);
+typedef void PR_CALLBACK GCBeginFinalizeHook(void *arg);
+typedef void PR_CALLBACK GCEndFinalizeHook(void *arg);
+typedef void PR_CALLBACK GCBeginGCHook(void *arg);
+typedef void PR_CALLBACK GCEndGCHook(void *arg);
+
+typedef enum { PR_GCBEGIN, PR_GCEND } GCLockHookArg;
+
+typedef void PR_CALLBACK GCLockHookFunc(GCLockHookArg arg1, void *arg2);
+
+typedef struct GCLockHook GCLockHook;
+
+struct GCLockHook {
+ GCLockHookFunc* func;
+ void* arg;
+ GCLockHook* next;
+ GCLockHook* prev;
+};
+
+
+/*
+** Hooks which are called at the beginning and end of the GC process.
+** The begin hooks are called before the root finding step. The hooks are
+** called with threading disabled, so it is now allowed to re-enter the
+** kernel. The end hooks are called after the gc has finished but before
+** the finalizer has run.
+*/
+PR_EXTERN(void) PR_CALLBACK PR_SetBeginGCHook(GCBeginGCHook *hook, void *arg);
+PR_EXTERN(void) PR_CALLBACK PR_GetBeginGCHook(GCBeginGCHook **hook, void **arg);
+PR_EXTERN(void) PR_CALLBACK PR_SetEndGCHook(GCBeginGCHook *hook, void *arg);
+PR_EXTERN(void) PR_CALLBACK PR_GetEndGCHook(GCEndGCHook **hook, void **arg);
+
+/*
+** Called before SuspendAll is called by dogc, so that GC thread can hold
+** all the locks before hand to avoid any deadlocks
+*/
+
+/*
+PR_EXTERN(void) PR_SetGCLockHook(GCLockHook *hook, void *arg);
+PR_EXTERN(void) PR_GetGCLockHook(GCLockHook **hook, void **arg);
+*/
+
+PR_EXTERN(int) PR_RegisterGCLockHook(GCLockHookFunc *hook, void *arg);
+
+/*
+** Hooks which are called at the beginning and end of the GC finalization
+** process. After the GC has identified all of the dead objects in the
+** heap, it looks for objects that need finalization. Before it calls the
+** first finalization proc (see the GCType structure above) it calls the
+** begin hook. When it has finalized the last object it calls the end
+** hook.
+*/
+PR_EXTERN(void) PR_SetBeginFinalizeHook(GCBeginFinalizeHook *hook, void *arg);
+PR_EXTERN(void) PR_GetBeginFinalizeHook(GCBeginFinalizeHook **hook, void **arg);
+PR_EXTERN(void) PR_SetEndFinalizeHook(GCBeginFinalizeHook *hook, void *arg);
+PR_EXTERN(void) PR_GetEndFinalizeHook(GCEndFinalizeHook **hook, void **arg);
+
+/*
+** Register a GC type. Return's the index into the GC internal type
+** table. The returned value is passed to PR_AllocMemory. After the call,
+** the "type" memory belongs to the GC (the caller must not free it or
+** change it).
+*/
+PR_EXTERN(PRInt32) PR_RegisterType(GCType *type);
+
+/*
+** Register a root finder with the collector. The collector will call
+** these functions to identify all of the roots before collection
+** proceeds. "arg" is passed to the function when it is called.
+*/
+PR_EXTERN(PRStatus) PR_RegisterRootFinder(GCRootFinder func, char *name, void *arg);
+
+/*
+** Allocate some GC'able memory. The object must be at least bytes in
+** size. The type index function for the object is specified. "flags"
+** specifies some control flags. If PR_ALLOC_CLEAN is set then the memory
+** is zero'd before being returned. If PR_ALLOC_DOUBLE is set then the
+** allocated memory is double aligned.
+**
+** Any memory cell that you store a pointer to something allocated by
+** this call must be findable by the GC. Use the PR_RegisterRootFinder to
+** register new places where the GC will look for pointers into the heap.
+** The GC already knows how to scan any NSPR threads or monitors.
+*/
+PR_EXTERN(PRWord GCPTR *)PR_AllocMemory(
+ PRWord bytes, PRInt32 typeIndex, PRWord flags);
+PR_EXTERN(PRWord GCPTR *)PR_AllocSimpleMemory(
+ PRWord bytes, PRInt32 typeIndex);
+
+/*
+** This function can be used to cause PR_AllocMemory to always return
+** NULL. This may be useful in low memory situations when we're trying to
+** shutdown applets.
+*/
+PR_EXTERN(void) PR_EnableAllocation(PRBool yesOrNo);
+
+/* flags bits */
+#define PR_ALLOC_CLEAN 0x1
+#define PR_ALLOC_DOUBLE 0x2
+#define PR_ALLOC_ZERO_HANDLE 0x4 /* XXX yes, it's a hack */
+
+/*
+** Force a garbage collection right now. Return when it completes.
+*/
+PR_EXTERN(void) PR_GC(void);
+
+/*
+** Force a finalization right now. Return when finalization has
+** completed. Finalization completes when there are no more objects
+** pending finalization. This does not mean there are no objects in the
+** gc heap that will need finalization should a collection be done after
+** this call.
+*/
+PR_EXTERN(void) PR_ForceFinalize(void);
+
+/*
+** Dump the GC heap out to the given file. This will stop the system dead
+** in its tracks while it is occuring.
+*/
+PR_EXTERN(void) PR_DumpGCHeap(FILE *out, PRBool detailed);
+
+/*
+** Wrapper for PR_DumpGCHeap
+*/
+PR_EXTERN(void) PR_DumpMemory(PRBool detailed);
+
+/*
+** Dump summary of objects allocated.
+*/
+PR_EXTERN(void) PR_DumpMemorySummary(void);
+
+/*
+** Dump the application heaps.
+*/
+PR_EXTERN(void) PR_DumpApplicationHeaps(void);
+
+/*
+** Helper function used by dump routines to do the indentation in a
+** consistent fashion.
+*/
+PR_EXTERN(void) PR_DumpIndent(FILE *out, PRIntn indent);
+
+/*
+** The GCInfo structure contains all of the GC state...
+**
+** busyMemory:
+** The amount of GC heap memory that is busy at this instant. Busy
+** doesn't mean alive, it just means that it has been
+** allocated. Immediately after a collection busy means how much is
+** alive.
+**
+** freeMemory:
+** The amount of GC heap memory that is as yet unallocated.
+**
+** allocMemory:
+** The sum of free and busy memory in the GC heap.
+**
+** maxMemory:
+** The maximum size that the GC heap is allowed to grow.
+**
+** lowSeg:
+** The lowest segment currently used in the GC heap.
+**
+** highSeg:
+** The highest segment currently used in the GC heap.
+** The lowSeg and highSeg members are used for a "quick test" of whether
+** a pointer falls within the GC heap. [ see GC_IN_HEAP(...) ]
+**
+** lock:
+** Monitor used for synchronization within the GC.
+**
+** finalizer:
+** Thread in which the GC finalizer is running.
+**
+** liveBlock:
+** Object scanning functions call through this function pointer to
+** register a potential block of pointers with the collector. (This is
+** currently not at all different than processRoot.)
+**
+** livePointer:
+** Object scanning functions call through this function pointer to
+** register a single pointer with the collector.
+**
+** processRootBlock:
+** When a root finder identifies a root it should call through this
+** function pointer so that the GC can process the root. The call takes
+** a base address and count which the gc will examine for valid heap
+** pointers.
+**
+** processRootPointer:
+** When a root finder identifies a root it should call through this
+** function pointer so that the GC can process the root. The call takes
+** a single pointer value.
+*/
+typedef struct GCInfoStr {
+ PRWord flags; /* trace flags (see below) */
+ PRWord busyMemory; /* memory in use right now */
+ PRWord freeMemory; /* memory free right now */
+ PRWord allocMemory; /* sum of busy & free memory */
+ PRWord maxMemory; /* max memory we are allowed to allocate */
+ PRWord *lowSeg; /* lowest segment in the GC heap */
+ PRWord *highSeg; /* highest segment in the GC heap */
+
+ PRMonitor *lock;
+ PRThread *finalizer;
+
+ void (PR_CALLBACK *liveBlock)(void **base, PRInt32 count);
+ void (PR_CALLBACK *livePointer)(void *ptr);
+ void (PR_CALLBACK *processRootBlock)(void **base, PRInt32 count);
+ void (PR_CALLBACK *processRootPointer)(void *ptr);
+ FILE* dumpOutput;
+#ifdef GCTIMINGHOOK
+ void (*gcTimingHook)(int32 gcTime);
+#endif
+} GCInfo;
+
+PR_EXTERN(GCInfo *) PR_GetGCInfo(void);
+PR_EXTERN(PRBool) PR_GC_In_Heap(void GCPTR *object);
+
+/*
+** Simple bounds check to see if a pointer is anywhere near the GC heap.
+** Used to avoid calls to PR_ProcessRoot and GCInfo.livePointer by object
+** scanning code.
+*/
+#if !defined(XP_PC) || defined(_WIN32)
+#define GC_IN_HEAP(_info, _p) (((PRWord*)(_p) >= (_info)->lowSeg) && \
+ ((PRWord*)(_p) < (_info)->highSeg))
+#else
+/*
+** The simple bounds check, above, doesn't work in Win16, because we don't
+** maintain: lowSeg == MIN(all segments) and highSeg == MAX(all segments).
+** So we have to do a little better.
+*/
+#define GC_IN_HEAP(_info, _p) PR_GC_In_Heap(_p)
+#endif
+
+PR_EXTERN(PRWord) PR_GetObjectHeader(void *ptr);
+
+PR_EXTERN(PRWord) PR_SetObjectHeader(void *ptr, PRWord newUserBits);
+
+/************************************************************************/
+
+/* Trace flags (passed to PR_InitGC or in environment GCLOG) */
+#define GC_TRACE 0x0001
+#define GC_ROOTS 0x0002
+#define GC_LIVE 0x0004
+#define GC_ALLOC 0x0008
+#define GC_MARK 0x0010
+#define GC_SWEEP 0x0020
+#define GC_DEBUG 0x0040
+#define GC_FINAL 0x0080
+
+#if defined(DEBUG_kipp) || defined(DEBUG_warren)
+#define GC_CHECK 0x0100
+#endif
+
+#ifdef DEBUG
+#define GCTRACE(x, y) if (PR_GetGCInfo()->flags & x) GCTrace y
+PR_EXTERN(void) GCTrace(char *fmt, ...);
+#else
+#define GCTRACE(x, y)
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* prgc_h___ */
diff --git a/lib/msgc/src/.cvsignore b/lib/msgc/src/.cvsignore
new file mode 100644
index 0000000..f3c7a7c
--- /dev/null
+++ b/lib/msgc/src/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/lib/msgc/src/Makefile.in b/lib/msgc/src/Makefile.in
new file mode 100644
index 0000000..bcc5862
--- /dev/null
+++ b/lib/msgc/src/Makefile.in
@@ -0,0 +1,93 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape Portable Runtime (NSPR).
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+INCLUDES = -I$(dist_includedir) -I../include
+
+CSRCS = prgcapi.c prmsgc.c
+
+ifeq ($(OS_ARCH),WINNT)
+CSRCS += win32gc.c
+else
+ifeq ($(OS_ARCH),OS2)
+CSRCS += os2gc.c
+else
+CSRCS += unixgc.c
+endif
+endif
+
+NSPR_VERSION = $(MOD_MAJOR_VERSION)
+
+EXTRA_LIBS = $(LIBNSPR)
+
+ifdef RESOLVE_LINK_SYMBOLS
+EXTRA_LIBS += $(OS_LIBS)
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+#RES=$(OBJDIR)/ds.res
+#RESNAME=$(MOD_DEPTH)/pr/src/nspr.rc
+#OS_LIBS = user32.lib
+endif # WINNT
+
+LIBRARY_NAME = msgc
+LIBRARY_VERSION = $(MOD_MAJOR_VERSION)
+
+RELEASE_LIBS = $(TARGETS)
+
+include $(topsrcdir)/config/rules.mk
+
+#
+# The Client build wants the shared libraries in $(dist_bindir),
+# so we also install them there.
+#
+
+export:: $(TARGETS)
+ $(INSTALL) -m 444 $(TARGETS) $(dist_libdir)
+ifdef SHARED_LIBRARY
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(dist_bindir)
+endif
diff --git a/lib/msgc/src/os2gc.c b/lib/msgc/src/os2gc.c
new file mode 100644
index 0000000..f99bb67
--- /dev/null
+++ b/lib/msgc/src/os2gc.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * GC related routines
+ *
+ */
+#include "prlog.h"
+
+#include <stdlib.h>
+
+/* Leave a bit of room for any malloc header bytes... */
+#define MAX_SEGMENT_SIZE (65536L - 4096L)
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+void _MD_InitGC() {}
+
+void *_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+
+ if ( *sizep > MAX_SEGMENT_SIZE )
+ {
+ *sizep = MAX_SEGMENT_SIZE;
+ }
+
+ addr = malloc((size_t)*sizep);
+ return addr;
+}
+
+
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ /* Not sure about this. Todd? */
+ return PR_FALSE;
+}
+
+
+void _MD_FreeGCSegment(void *base, PRInt32 len)
+{
+ if (base)
+ {
+ free(base);
+ }
+}
diff --git a/lib/msgc/src/prgcapi.c b/lib/msgc/src/prgcapi.c
new file mode 100644
index 0000000..324ac7b
--- /dev/null
+++ b/lib/msgc/src/prgcapi.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include "prenv.h"
+#include "prmem.h"
+#include "prmon.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "private/pprthred.h"
+#include "gcint.h"
+
+/*
+** Generic GC implementation independent code for the NSPR GC
+*/
+
+RootFinder *_pr_rootFinders;
+
+CollectorType *_pr_collectorTypes;
+
+/* GC State information */
+GCInfo _pr_gcData;
+
+GCBeginGCHook *_pr_beginGCHook;
+void *_pr_beginGCHookArg;
+GCBeginGCHook *_pr_endGCHook;
+void *_pr_endGCHookArg;
+
+GCBeginFinalizeHook *_pr_beginFinalizeHook;
+void *_pr_beginFinalizeHookArg;
+GCBeginFinalizeHook *_pr_endFinalizeHook;
+void *_pr_endFinalizeHookArg;
+
+FILE *_pr_dump_file;
+int _pr_do_a_dump;
+GCLockHook *_pr_GCLockHook;
+
+extern PRLogModuleInfo *_pr_msgc_lm;
+
+/************************************************************************/
+
+static PRStatus PR_CALLBACK
+pr_ScanOneThread(PRThread* t, void** addr, PRUword count, void* closure)
+{
+ _pr_gcData.processRootBlock(addr, count);
+ return PR_SUCCESS;
+}
+
+/*
+** Scan all of the threads C stack's and registers, looking for "root"
+** pointers into the GC heap. These are the objects that the GC cannot
+** move and are considered "live" by the GC. Caller has stopped all of
+** the threads from running.
+*/
+static void PR_CALLBACK ScanThreads(void *arg)
+{
+ PR_ScanStackPointers(pr_ScanOneThread, arg);
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(GCInfo *) PR_GetGCInfo(void)
+{
+ return &_pr_gcData;
+}
+
+
+PR_IMPLEMENT(PRInt32) PR_RegisterType(GCType *t)
+{
+ CollectorType *ct, *ect;
+ int rv = -1;
+
+ LOCK_GC();
+ ct = &_pr_collectorTypes[0];
+ ect = &_pr_collectorTypes[FREE_MEMORY_TYPEIX];
+ for (; ct < ect; ct++) {
+ if (ct->flags == 0) {
+ ct->gctype = *t;
+ ct->flags = _GC_TYPE_BUSY;
+ if (0 != ct->gctype.finalize) {
+ ct->flags |= _GC_TYPE_FINAL;
+ }
+ if (0 != ct->gctype.getWeakLinkOffset) {
+ ct->flags |= _GC_TYPE_WEAK;
+ }
+ rv = ct - &_pr_collectorTypes[0];
+ break;
+ }
+ }
+ UNLOCK_GC();
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_RegisterRootFinder(
+ GCRootFinder f, char *name, void *arg)
+{
+ RootFinder *rf = PR_NEWZAP(RootFinder);
+ if (rf) {
+ rf->func = f;
+ rf->name = name;
+ rf->arg = arg;
+
+ LOCK_GC();
+ rf->next = _pr_rootFinders;
+ _pr_rootFinders = rf;
+ UNLOCK_GC();
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+
+
+PR_IMPLEMENT(int) PR_RegisterGCLockHook(GCLockHookFunc* f, void *arg)
+{
+
+ GCLockHook *rf = 0;
+
+ rf = (GCLockHook*) calloc(1, sizeof(GCLockHook));
+ if (rf) {
+ rf->func = f;
+ rf->arg = arg;
+
+ LOCK_GC();
+ /* first dummy node */
+ if (! _pr_GCLockHook) {
+ _pr_GCLockHook = (GCLockHook*) calloc(1, sizeof(GCLockHook));
+ _pr_GCLockHook->next = _pr_GCLockHook;
+ _pr_GCLockHook->prev = _pr_GCLockHook;
+ }
+
+ rf->next = _pr_GCLockHook;
+ rf->prev = _pr_GCLockHook->prev;
+ _pr_GCLockHook->prev->next = rf;
+ _pr_GCLockHook->prev = rf;
+ UNLOCK_GC();
+ return 0;
+ }
+ return -1;
+}
+
+/*
+PR_IMPLEMENT(void) PR_SetGCLockHook(GCLockHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_GCLockHook = hook;
+ _pr_GCLockHookArg2 = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetGCLockHook(GCLockHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_GCLockHook;
+ *arg = _pr_GCLockHookArg2;
+ UNLOCK_GC();
+}
+*/
+
+
+PR_IMPLEMENT(void) PR_SetBeginGCHook(GCBeginGCHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_beginGCHook = hook;
+ _pr_beginGCHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetBeginGCHook(GCBeginGCHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_beginGCHook;
+ *arg = _pr_beginGCHookArg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_SetEndGCHook(GCEndGCHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_endGCHook = hook;
+ _pr_endGCHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetEndGCHook(GCEndGCHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_endGCHook;
+ *arg = _pr_endGCHookArg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_SetBeginFinalizeHook(GCBeginFinalizeHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_beginFinalizeHook = hook;
+ _pr_beginFinalizeHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetBeginFinalizeHook(GCBeginFinalizeHook **hook,
+ void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_beginFinalizeHook;
+ *arg = _pr_beginFinalizeHookArg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_SetEndFinalizeHook(GCEndFinalizeHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_endFinalizeHook = hook;
+ _pr_endFinalizeHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetEndFinalizeHook(GCEndFinalizeHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_endFinalizeHook;
+ *arg = _pr_endFinalizeHookArg;
+ UNLOCK_GC();
+}
+
+#ifdef DEBUG
+#include "prprf.h"
+
+PR_IMPLEMENT(void) GCTrace(char *fmt, ...)
+{
+ va_list ap;
+ char buf[400];
+
+ va_start(ap, fmt);
+ PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("%s", buf));
+}
+#endif
+
+void _PR_InitGC(PRWord flags)
+{
+ static char firstTime = 1;
+
+ if (!firstTime) return;
+ firstTime = 0;
+
+ _MD_InitGC();
+
+ if (flags == 0) {
+ char *ev = PR_GetEnv("GCLOG");
+ if (ev && ev[0]) {
+ flags = atoi(ev);
+ }
+ }
+ _pr_gcData.flags = flags;
+
+ _pr_gcData.lock = PR_NewMonitor();
+
+ _pr_collectorTypes = (CollectorType*) PR_CALLOC(256 * sizeof(CollectorType));
+
+ PR_RegisterRootFinder(ScanThreads, "scan threads", 0);
+ PR_RegisterRootFinder(_PR_ScanFinalQueue, "scan final queue", 0);
+}
+
+extern void pr_FinalizeOnExit(void);
+
+#ifdef DEBUG
+#ifdef GC_STATS
+PR_PUBLIC_API(void) PR_PrintGCAllocStats(void);
+#endif
+#endif
+
+PR_IMPLEMENT(void)
+PR_ShutdownGC(PRBool finalizeOnExit)
+{
+ /* first finalize all the objects in the heap */
+ if (finalizeOnExit) {
+ pr_FinalizeOnExit();
+ }
+
+#ifdef DEBUG
+#ifdef GC_STATS
+ PR_PrintGCAllocStats();
+#endif /* GC_STATS */
+#endif /* DEBUG */
+
+ /* then the chance for any future allocations */
+
+ /* finally delete the gc heap */
+
+ /* write me */
+}
+
+/******************************************************************************/
diff --git a/lib/msgc/src/prmsgc.c b/lib/msgc/src/prmsgc.c
new file mode 100644
index 0000000..2c1e853
--- /dev/null
+++ b/lib/msgc/src/prmsgc.c
@@ -0,0 +1,3320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <string.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <time.h>
+
+#ifdef WIN32
+#include <windef.h>
+#include <winbase.h>
+#endif
+
+#include "prclist.h"
+#include "prbit.h"
+
+#include "prtypes.h"
+#include "prenv.h"
+#include "prgc.h"
+#include "prthread.h"
+#include "prlog.h"
+#include "prlong.h"
+#include "prinrval.h"
+#include "prprf.h"
+#include "gcint.h"
+
+#include "private/pprthred.h"
+
+typedef void (*PRFileDumper)(FILE *out, PRBool detailed);
+
+PR_EXTERN(void)
+PR_DumpToFile(char* filename, char* msg, PRFileDumper dump, PRBool detailed);
+
+/*
+** Mark&sweep garbage collector. Supports objects that require
+** finalization, objects that can have a single weak link, and special
+** objects that require care during sweeping.
+*/
+
+PRLogModuleInfo *_pr_msgc_lm;
+PRLogModuleInfo* GC;
+
+static PRInt32 _pr_pageShift;
+static PRInt32 _pr_pageSize;
+
+#ifdef DEBUG
+#define GCMETER
+#endif
+#ifdef DEBUG_jwz
+# undef GCMETER
+#endif /* 1 */
+
+#ifdef GCMETER
+#define METER(x) x
+#else
+#define METER(x)
+#endif
+
+/*
+** Make this constant bigger to reduce the amount of recursion during
+** garbage collection.
+*/
+#define MAX_SCAN_Q 100L
+
+#if defined(XP_PC) && !defined(WIN32)
+#define MAX_SEGS 400L
+#define MAX_SEGMENT_SIZE (65536L - 4096L)
+#define SEGMENT_SIZE (65536L - 4096L)
+#define MAX_ALLOC_SIZE (65536L - 4096L)
+#else
+#define MAX_SEGS 400L
+#define MAX_SEGMENT_SIZE (2L * 256L * 1024L)
+#define SEGMENT_SIZE (1L * 256L * 1024L)
+#define MAX_ALLOC_SIZE (4L * 1024L * 1024L)
+#endif
+
+/*
+ * The highest value that can fit into a signed integer. This
+ * is used to prevent overflow of allocation size in alloc routines.
+ */
+
+#define MAX_INT ((1UL << (PR_BITS_PER_INT - 1)) - 1)
+
+/*
+ * On 32-bit machines, only 22 bits are used in the cibx integer to
+ * store size since 8 bits of the integer are used to store type, and
+ * of the remainder, 2 are user defined. Max allocation size = 2^22 -1
+ */
+
+#define MAX_ALLOC ( (1L << (PR_BYTES_PER_WORD_LOG2 + WORDS_BITS )) -1)
+
+/* The minimum percentage of free heap space after a collection. If
+ the amount of free space doesn't meet this criteria then we will
+ attempt to grow the heap */
+#define MIN_FREE_THRESHOLD_AFTER_GC 20L
+
+static PRInt32 segmentSize = SEGMENT_SIZE;
+
+static PRInt32 collectorCleanupNeeded;
+
+#ifdef GCMETER
+PRUint32 _pr_gcMeter;
+
+#define _GC_METER_STATS 0x01L
+#define _GC_METER_GROWTH 0x02L
+#define _GC_METER_FREE_LIST 0x04L
+#endif
+
+/************************************************************************/
+
+#define LINEAR_BIN_EXPONENT 5
+#define NUM_LINEAR_BINS ((PRUint32)1 << LINEAR_BIN_EXPONENT)
+#define FIRST_LOG_BIN (NUM_LINEAR_BINS - LINEAR_BIN_EXPONENT)
+
+/* Each free list bin holds a chunk of memory sized from
+ 2^n to (2^(n+1))-1 inclusive. */
+#define NUM_BINS (FIRST_LOG_BIN + 32)
+
+/*
+ * Find the bin number for a given size (in bytes). This does not round up as
+ * values from 2^n to (2^(n+1))-1 share the same bin.
+ */
+#define InlineBinNumber(_bin,_bytes) \
+{ \
+ PRUint32 _t, _n = (PRUint32) _bytes / 4; \
+ if (_n < NUM_LINEAR_BINS) { \
+ _bin = _n; \
+ } else { \
+ _bin = FIRST_LOG_BIN; \
+ 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 ((_n >> 1) != 0) _bin++; \
+ } \
+}
+
+#define BIG_ALLOC 16384L
+
+#define MIN_FREE_CHUNK_BYTES ((PRInt32)sizeof(GCFreeChunk))
+
+/* Note: fix code in PR_AllocMemory if you change the size of GCFreeChunk
+ so that it zeros the right number of words */
+typedef struct GCFreeChunk {
+ struct GCFreeChunk *next;
+ struct GCSeg *segment;
+ PRInt32 chunkSize;
+} GCFreeChunk;
+
+typedef struct GCSegInfo {
+ struct GCSegInfo *next;
+ char *base;
+ char *limit;
+ PRWord *hbits;
+ int fromMalloc;
+} GCSegInfo;
+
+typedef struct GCSeg {
+ char *base;
+ char *limit;
+ PRWord *hbits;
+ GCSegInfo *info;
+} GCSeg;
+
+#ifdef GCMETER
+typedef struct GCMeter {
+ PRInt32 allocBytes;
+ PRInt32 wastedBytes;
+ PRInt32 numFreeChunks;
+ PRInt32 skippedFreeChunks;
+} GCMeter;
+static GCMeter meter;
+#endif
+
+/*
+** There is one of these for each segment of GC'able memory.
+*/
+static GCSeg segs[MAX_SEGS];
+static GCSegInfo *freeSegs;
+static GCSeg* lastInHeap;
+static int nsegs;
+
+static GCFreeChunk *bins[NUM_BINS];
+static PRInt32 minBin;
+static PRInt32 maxBin;
+
+/*
+** Scan Q used to avoid deep recursion when scanning live objects for
+** heap pointers
+*/
+typedef struct GCScanQStr {
+ PRWord *q[MAX_SCAN_Q];
+ int queued;
+} GCScanQ;
+
+static GCScanQ *pScanQ;
+
+#ifdef GCMETER
+PRInt32 _pr_maxScanDepth;
+PRInt32 _pr_scanDepth;
+#endif
+
+/*
+** Keeps track of the number of bytes allocated via the BigAlloc()
+** allocator. When the number of bytes allocated, exceeds the
+** BIG_ALLOC_GC_SIZE, then a GC will occur before the next allocation
+** is done...
+*/
+#define BIG_ALLOC_GC_SIZE (4*SEGMENT_SIZE)
+static PRWord bigAllocBytes = 0;
+
+/*
+** There is one GC header word in front of each GC allocated object. We
+** use it to contain information about the object (what TYPEIX to use for
+** scanning it, how big it is, it's mark status, and if it's a root).
+*/
+#define TYPEIX_BITS 8L
+#define WORDS_BITS 20L
+#define MAX_CBS (1L << GC_TYPEIX_BITS)
+#define MAX_WORDS (1L << GC_WORDS_BITS)
+#define TYPEIX_SHIFT 24L
+#define MAX_TYPEIX ((1L << TYPEIX_BITS) - 1L)
+#define TYPEIX_MASK PR_BITMASK(TYPEIX_BITS)
+#define WORDS_SHIFT 2L
+#define WORDS_MASK PR_BITMASK(WORDS_BITS)
+#define MARK_BIT 1L
+#define FINAL_BIT 2L
+
+/* Two bits per object header are reserved for the user of the memory
+ system to store information into. */
+#define GC_USER_BITS_SHIFT 22L
+#define GC_USER_BITS 0x00c00000L
+
+#define MAKE_HEADER(_cbix,_words) \
+ ((PRWord) (((unsigned long)(_cbix) << TYPEIX_SHIFT) \
+ | ((unsigned long)(_words) << WORDS_SHIFT)))
+
+#define GET_TYPEIX(_h) \
+ (((PRUword)(_h) >> TYPEIX_SHIFT) & 0xff)
+
+#define MARK(_sp,_p) \
+ (((PRWord *)(_p))[0] |= MARK_BIT)
+#define IS_MARKED(_sp,_p) \
+ (((PRWord *)(_p))[0] & MARK_BIT)
+#define OBJ_BYTES(_h) \
+ (((PRInt32) (_h) & 0x003ffffcL) << (PR_BYTES_PER_WORD_LOG2-2L))
+
+#define GC_GET_USER_BITS(_h) (((_h) & GC_USER_BITS) >> GC_USER_BITS_SHIFT)
+
+/************************************************************************/
+
+/*
+** Mark the start of an object in a segment. Note that we mark the header
+** word (which we always have), not the data word (which we may not have
+** for empty objects).
+** XXX tune: put subtract of _sp->base into _sp->hbits pointer?
+*/
+#define SET_HBIT(_sp,_ph) \
+ SET_BIT((_sp)->hbits, (((PRWord*)(_ph)) - ((PRWord*) (_sp)->base)))
+
+#define CLEAR_HBIT(_sp,_ph) \
+ CLEAR_BIT((_sp)->hbits, (((PRWord*)(_ph)) - ((PRWord*) (_sp)->base)))
+
+#define IS_HBIT(_sp,_ph) \
+ TEST_BIT((_sp)->hbits, (((PRWord*)(_ph)) - ((PRWord*) (_sp)->base)))
+
+/*
+** Given a pointer into this segment, back it up until we are at the
+** start of the object the pointer points into. Each heap segment has a
+** bitmap that has one bit for each word of the objects it contains. The
+** bit's are set for the firstword of an object, and clear for it's other
+** words.
+*/
+static PRWord *FindObject(GCSeg *sp, PRWord *p)
+{
+ PRWord *base;
+
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p & ~(PR_BYTES_PER_WORD-1L));
+
+ base = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ return (p);
+ }
+ p--;
+ } while ( p >= base );
+
+ /* Heap is corrupted! */
+ _GCTRACE(GC_TRACE, ("ERROR: The heap is corrupted!!! aborting now!"));
+ abort();
+ return NULL;
+}
+
+/************************************************************************/
+#if !defined(XP_PC) || defined(XP_OS2)
+#define OutputDebugString(msg)
+#endif
+
+#define IN_SEGMENT(_sp, _p) \
+ ((((char *)(_p)) >= (_sp)->base) && \
+ (((char *)(_p)) < (_sp)->limit))
+
+static GCSeg *InHeap(void *p)
+{
+ GCSeg *sp, *esp;
+
+ if (lastInHeap && IN_SEGMENT(lastInHeap, p)) {
+ return lastInHeap;
+ }
+
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p)) {
+ lastInHeap = sp;
+ return sp;
+ }
+ }
+ return 0;
+}
+
+/*
+** Grow the heap by allocating another segment. Fudge the requestedSize
+** value to try to pre-account for the HBITS.
+*/
+static GCSeg* DoGrowHeap(PRInt32 requestedSize, PRBool exactly)
+{
+ GCSeg *sp;
+ GCSegInfo *segInfo;
+ GCFreeChunk *cp;
+ char *base;
+ PRWord *hbits;
+ PRInt32 nhbytes, nhbits;
+ PRUint32 allocSize;
+
+ if (nsegs == MAX_SEGS) {
+ /* No room for more segments */
+ return 0;
+ }
+
+ segInfo = (GCSegInfo*) PR_MALLOC(sizeof(GCSegInfo));
+#ifdef DEBUG
+ {
+ char str[256];
+ sprintf(str, "[1] Allocated %ld bytes at %p\n",
+ (long) sizeof(GCSegInfo), segInfo);
+ OutputDebugString(str);
+ }
+#endif
+ if (!segInfo) {
+ return 0;
+ }
+
+ /* Get more memory from the OS */
+ if (exactly) {
+ allocSize = requestedSize;
+ base = (char *) PR_MALLOC(requestedSize);
+ } else {
+ allocSize = requestedSize;
+ allocSize = (allocSize + _pr_pageSize - 1L) >> _pr_pageShift;
+ allocSize <<= _pr_pageShift;
+ base = (char*)_MD_GrowGCHeap(&allocSize);
+ }
+ if (!base) {
+ PR_DELETE(segInfo);
+ return 0;
+ }
+
+ nhbits = (PRInt32)(
+ (allocSize + PR_BYTES_PER_WORD - 1L) >> PR_BYTES_PER_WORD_LOG2);
+ nhbytes = ((nhbits + PR_BITS_PER_WORD - 1L) >> PR_BITS_PER_WORD_LOG2)
+ * sizeof(PRWord);
+
+ /* Get bitmap memory from malloc heap */
+ hbits = (PRWord *) PR_CALLOC((PRUint32)nhbytes);
+ if (!hbits) {
+ /* Loser! */
+ PR_DELETE(segInfo);
+ if (exactly) {
+ PR_DELETE(base);
+ } else {
+ /* XXX do something about this */
+ /* _MD_FreeGCSegment(base, allocSize); */
+ }
+ return 0;
+ }
+
+ /*
+ ** Setup new segment.
+ */
+ sp = &segs[nsegs++];
+ segInfo->base = sp->base = base;
+ segInfo->limit = sp->limit = base + allocSize;
+ segInfo->hbits = sp->hbits = hbits;
+ sp->info = segInfo;
+ segInfo->fromMalloc = exactly;
+ memset(base, 0, allocSize);
+
+#ifdef GCMETER
+ if (_pr_gcMeter & _GC_METER_GROWTH) {
+ fprintf(stderr, "[GC: new segment base=%p size=%ld]\n",
+ sp->base, (long) allocSize);
+ }
+#endif
+
+ _pr_gcData.allocMemory += allocSize;
+ _pr_gcData.freeMemory += allocSize;
+
+ if (!exactly) {
+ PRInt32 bin;
+
+ /* Put free memory into a freelist bin */
+ cp = (GCFreeChunk *) base;
+ cp->segment = sp;
+ cp->chunkSize = allocSize;
+ InlineBinNumber(bin, allocSize)
+ cp->next = bins[bin];
+ bins[bin] = cp;
+ if (bin < minBin) minBin = bin;
+ if (bin > maxBin) maxBin = bin;
+ } else {
+ /*
+ ** When exactly allocating the entire segment is given over to a
+ ** single object to prevent fragmentation
+ */
+ }
+
+ if (!_pr_gcData.lowSeg) {
+ _pr_gcData.lowSeg = (PRWord*) sp->base;
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ } else {
+ if ((PRWord*)sp->base < _pr_gcData.lowSeg) {
+ _pr_gcData.lowSeg = (PRWord*) sp->base;
+ }
+ if ((PRWord*)sp->limit > _pr_gcData.highSeg) {
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ }
+ }
+
+ /*
+ ** Get rid of the GC pointer in case it shows up in some uninitialized
+ ** local stack variable later (while scanning the C stack looking for
+ ** roots).
+ */
+ memset(&base, 0, sizeof(base)); /* optimizers beware */
+
+ PR_LOG(_pr_msgc_lm, PR_LOG_WARNING, ("grow heap: total gc memory now %d",
+ _pr_gcData.allocMemory));
+
+ return sp;
+}
+
+#ifdef USE_EXTEND_HEAP
+static PRBool ExtendHeap(PRInt32 requestedSize) {
+ GCSeg* sp;
+ PRUint32 allocSize;
+ PRInt32 oldSize, newSize;
+ PRInt32 newHBits, newHBytes;
+ PRInt32 oldHBits, oldHBytes;
+ PRWord* hbits;
+ GCFreeChunk* cp;
+ PRInt32 bin;
+
+ /* Can't extend nothing */
+ if (nsegs == 0) return PR_FALSE;
+
+ /* Round up requested size to the size of a page */
+ allocSize = (PRUint32) requestedSize;
+ allocSize = (allocSize + _pr_pageSize - 1L) >> _pr_pageShift;
+ allocSize <<= _pr_pageShift;
+
+ /* Malloc some memory for the new hbits array */
+ sp = segs;
+ oldSize = sp->limit - sp->base;
+ newSize = oldSize + allocSize;
+ newHBits = (newSize + PR_BYTES_PER_WORD - 1L) >> PR_BYTES_PER_WORD_LOG2;
+ newHBytes = ((newHBits + PR_BITS_PER_WORD - 1L) >> PR_BITS_PER_WORD_LOG2)
+ * sizeof(PRWord);
+ hbits = (PRWord*) PR_MALLOC(newHBytes);
+ if (0 == hbits) return PR_FALSE;
+
+ /* Attempt to extend the last segment by the desired amount */
+ if (_MD_ExtendGCHeap(sp->base, oldSize, newSize)) {
+ oldHBits = (oldSize + PR_BYTES_PER_WORD - 1L) >> PR_BYTES_PER_WORD_LOG2;
+ oldHBytes = ((oldHBits + PR_BITS_PER_WORD - 1L) >> PR_BITS_PER_WORD_LOG2)
+ * sizeof(PRWord);
+
+ /* Copy hbits from old memory into new memory */
+ memset(hbits, 0, newHBytes);
+ memcpy(hbits, sp->hbits, oldHBytes);
+ PR_DELETE(sp->hbits);
+ memset(sp->base + oldSize, 0, allocSize);
+
+ /* Adjust segment state */
+ sp->limit += allocSize;
+ sp->hbits = hbits;
+ sp->info->limit = sp->limit;
+ sp->info->hbits = hbits;
+
+ /* Put free memory into a freelist bin */
+ cp = (GCFreeChunk *) (sp->base + oldSize);
+ cp->segment = sp;
+ cp->chunkSize = allocSize;
+ InlineBinNumber(bin, allocSize)
+ cp->next = bins[bin];
+ bins[bin] = cp;
+ if (bin < minBin) minBin = bin;
+ if (bin > maxBin) maxBin = bin;
+
+ /* Prevent a pointer that points to the free memory from showing
+ up on the call stack later on */
+ memset(&cp, 0, sizeof(cp));
+
+ /* Update heap brackets and counters */
+ if ((PRWord*)sp->limit > _pr_gcData.highSeg) {
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ }
+ _pr_gcData.allocMemory += allocSize;
+ _pr_gcData.freeMemory += allocSize;
+
+ return PR_TRUE;
+ }
+ PR_DELETE(hbits);
+ return PR_FALSE;
+}
+#endif /* USE_EXTEND_HEAP */
+
+static GCSeg *GrowHeapExactly(PRInt32 requestedSize)
+{
+ GCSeg *sp = DoGrowHeap(requestedSize, PR_TRUE);
+ return sp;
+}
+
+static PRBool GrowHeap(PRInt32 requestedSize)
+{
+ void *p;
+#ifdef USE_EXTEND_HEAP
+ if (ExtendHeap(requestedSize)) {
+ return PR_TRUE;
+ }
+#endif
+ p = DoGrowHeap(requestedSize, PR_FALSE);
+ return (p != NULL ? PR_TRUE : PR_FALSE);
+}
+
+/*
+** Release a segment when it is entirely free.
+*/
+static void ShrinkGCHeap(GCSeg *sp)
+{
+#ifdef GCMETER
+ if (_pr_gcMeter & _GC_METER_GROWTH) {
+ fprintf(stderr, "[GC: free segment base=%p size=%ld]\n",
+ sp->base, (long) (sp->limit - sp->base));
+ }
+#endif
+
+ /*
+ * Put segment onto free seginfo list (we can't call free right now
+ * because we have the GC lock and all of the other threads are
+ * suspended; if one of them has the malloc lock we would deadlock)
+ */
+ sp->info->next = freeSegs;
+ freeSegs = sp->info;
+ collectorCleanupNeeded = 1;
+ _pr_gcData.allocMemory -= sp->limit - sp->base;
+ if (sp == lastInHeap) lastInHeap = 0;
+
+ /* Squish out disappearing segment from segment table */
+ --nsegs;
+ if ((sp - segs) != nsegs) {
+ *sp = segs[nsegs];
+ } else {
+ sp->base = 0;
+ sp->limit = 0;
+ sp->hbits = 0;
+ sp->info = 0;
+ }
+
+ /* Recalculate the lowSeg and highSeg values */
+ _pr_gcData.lowSeg = (PRWord*) segs[0].base;
+ _pr_gcData.highSeg = (PRWord*) segs[0].limit;
+ for (sp = segs; sp < &segs[nsegs]; sp++) {
+ if ((PRWord*)sp->base < _pr_gcData.lowSeg) {
+ _pr_gcData.lowSeg = (PRWord*) sp->base;
+ }
+ if ((PRWord*)sp->limit > _pr_gcData.highSeg) {
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ }
+ }
+}
+
+static void FreeSegments(void)
+{
+ GCSegInfo *si;
+
+ while (0 != freeSegs) {
+ LOCK_GC();
+ si = freeSegs;
+ if (si) {
+ freeSegs = si->next;
+ }
+ UNLOCK_GC();
+
+ if (!si) {
+ break;
+ }
+ PR_DELETE(si->base);
+ PR_DELETE(si->hbits);
+ PR_DELETE(si);
+ }
+}
+
+/************************************************************************/
+
+void ScanScanQ(GCScanQ *iscan)
+{
+ PRWord *p;
+ PRWord **pp;
+ PRWord **epp;
+ GCScanQ nextQ, *scan, *next, *temp;
+ CollectorType *ct;
+
+ if (!iscan->queued) return;
+
+ _GCTRACE(GC_MARK, ("begin scanQ @ 0x%x (%d)", iscan, iscan->queued));
+ scan = iscan;
+ next = &nextQ;
+ while (scan->queued) {
+ _GCTRACE(GC_MARK, ("continue scanQ @ 0x%x (%d)", scan, scan->queued));
+ /*
+ * Set pointer to current scanQ so that _pr_gcData.livePointer
+ * can find it.
+ */
+ pScanQ = next;
+ next->queued = 0;
+
+ /* Now scan the scan Q */
+ pp = scan->q;
+ epp = &scan->q[scan->queued];
+ scan->queued = 0;
+ while (pp < epp) {
+ p = *pp++;
+ ct = &_pr_collectorTypes[GET_TYPEIX(p[0])];
+ PR_ASSERT(0 != ct->gctype.scan);
+ /* Scan object ... */
+ (*ct->gctype.scan)(p + 1);
+ }
+
+ /* Exchange pointers so that we scan next */
+ temp = scan;
+ scan = next;
+ next = temp;
+ }
+
+ pScanQ = iscan;
+ PR_ASSERT(nextQ.queued == 0);
+ PR_ASSERT(iscan->queued == 0);
+}
+
+/*
+** Called during root finding step to identify "root" pointers into the
+** GC heap. First validate if it is a real heap pointer and then mark the
+** object being pointed to and add it to the scan Q for eventual
+** scanning.
+*/
+static void PR_CALLBACK ProcessRootBlock(void **base, PRInt32 count)
+{
+ GCSeg *sp;
+ PRWord *p0, *p, h, tix, *low, *high, *segBase;
+ CollectorType *ct;
+#ifdef DEBUG
+ void **base0 = base;
+#endif
+
+ low = _pr_gcData.lowSeg;
+ high = _pr_gcData.highSeg;
+ while (--count >= 0) {
+ p0 = (PRWord*) *base++;
+ if (p0 < low) continue; /* below gc heap */
+ if (p0 >= high) continue; /* above gc heap */
+ /* NOTE: inline expansion of InHeap */
+ /* Find segment */
+ sp = lastInHeap;
+ if (!sp || !IN_SEGMENT(sp,p0)) {
+ GCSeg *esp;
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p0)) {
+ lastInHeap = sp;
+ goto find_object;
+ }
+ }
+ continue;
+ }
+
+ find_object:
+ /* NOTE: Inline expansion of FindObject */
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p0 & ~(PR_BYTES_PER_WORD-1L));
+ segBase = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ goto winner;
+ }
+ p--;
+ } while (p >= segBase);
+
+ /*
+ ** We have a pointer into the heap, but it has no header
+ ** bit. This means that somehow the very first object in the heap
+ ** doesn't have a header. This is impossible so when debugging
+ ** lets abort.
+ */
+#ifdef DEBUG
+ PR_Abort();
+#endif
+
+ winner:
+ h = p[0];
+ if ((h & MARK_BIT) == 0) {
+#ifdef DEBUG
+ _GCTRACE(GC_ROOTS,
+ ("root 0x%p (%d) base0=%p off=%d",
+ p, OBJ_BYTES(h), base0, (base-1) - base0));
+#endif
+
+ /* Mark the root we just found */
+ p[0] = h | MARK_BIT;
+
+ /*
+ * See if object we just found needs scanning. It must
+ * have a scan function to be placed on the scanQ.
+ */
+ tix = (PRWord)GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ if (0 == ct->gctype.scan) {
+ continue;
+ }
+
+ /*
+ ** Put a pointer onto the scan Q. We use the scan Q to avoid
+ ** deep recursion on the C call stack. Objects are added to
+ ** the scan Q until the scan Q fills up. At that point we
+ ** make a call to ScanScanQ which proceeds to scan each of
+ ** the objects in the Q. This limits the recursion level by a
+ ** large amount though the stack frames get larger to hold
+ ** the GCScanQ's.
+ */
+ pScanQ->q[pScanQ->queued++] = p;
+ if (pScanQ->queued == MAX_SCAN_Q) {
+ METER(_pr_scanDepth++);
+ ScanScanQ(pScanQ);
+ }
+ }
+ }
+}
+
+static void PR_CALLBACK ProcessRootPointer(void *ptr)
+{
+ PRWord *p0, *p, h, tix, *segBase;
+ GCSeg* sp;
+ CollectorType *ct;
+
+ p0 = (PRWord*) ptr;
+
+ if (p0 < _pr_gcData.lowSeg) return; /* below gc heap */
+ if (p0 >= _pr_gcData.highSeg) return; /* above gc heap */
+
+ /* NOTE: inline expansion of InHeap */
+ /* Find segment */
+ sp = lastInHeap;
+ if (!sp || !IN_SEGMENT(sp,p0)) {
+ GCSeg *esp;
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p0)) {
+ lastInHeap = sp;
+ goto find_object;
+ }
+ }
+ return;
+ }
+
+ find_object:
+ /* NOTE: Inline expansion of FindObject */
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p0 & ~(BYTES_PER_WORD-1L));
+ segBase = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ goto winner;
+ }
+ p--;
+ } while (p >= segBase);
+
+ /*
+ ** We have a pointer into the heap, but it has no header
+ ** bit. This means that somehow the very first object in the heap
+ ** doesn't have a header. This is impossible so when debugging
+ ** lets abort.
+ */
+#ifdef DEBUG
+ PR_Abort();
+#endif
+
+ winner:
+ h = p[0];
+ if ((h & MARK_BIT) == 0) {
+#ifdef DEBUG
+ _GCTRACE(GC_ROOTS, ("root 0x%p (%d)", p, OBJ_BYTES(h)));
+#endif
+
+ /* Mark the root we just found */
+ p[0] = h | MARK_BIT;
+
+ /*
+ * See if object we just found needs scanning. It must
+ * have a scan function to be placed on the scanQ.
+ */
+ tix = (PRWord)GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ if (0 == ct->gctype.scan) {
+ return;
+ }
+
+ /*
+ ** Put a pointer onto the scan Q. We use the scan Q to avoid
+ ** deep recursion on the C call stack. Objects are added to
+ ** the scan Q until the scan Q fills up. At that point we
+ ** make a call to ScanScanQ which proceeds to scan each of
+ ** the objects in the Q. This limits the recursion level by a
+ ** large amount though the stack frames get larger to hold
+ ** the GCScanQ's.
+ */
+ pScanQ->q[pScanQ->queued++] = p;
+ if (pScanQ->queued == MAX_SCAN_Q) {
+ METER(_pr_scanDepth++);
+ ScanScanQ(pScanQ);
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Empty the freelist for each segment. This is done to make sure that
+** the root finding step works properly (otherwise, if we had a pointer
+** into a free section, we might not find its header word and abort in
+** FindObject)
+*/
+static void EmptyFreelists(void)
+{
+ GCFreeChunk *cp;
+ GCFreeChunk *next;
+ GCSeg *sp;
+ PRWord *p;
+ PRInt32 chunkSize;
+ PRInt32 bin;
+
+ /*
+ ** Run over the freelist and make all of the free chunks look like
+ ** object debris.
+ */
+ for (bin = 0; bin <= NUM_BINS-1; bin++) {
+ cp = bins[bin];
+ while (cp) {
+ next = cp->next;
+ sp = cp->segment;
+ chunkSize = cp->chunkSize >> BYTES_PER_WORD_LOG2;
+ p = (PRWord*) cp;
+ PR_ASSERT(chunkSize != 0);
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, chunkSize);
+ SET_HBIT(sp, p);
+ cp = next;
+ }
+ bins[bin] = 0;
+ }
+ minBin = NUM_BINS - 1;
+ maxBin = 0;
+}
+
+typedef struct GCBlockEnd {
+ PRInt32 check;
+#ifdef GC_CHECK
+ PRInt32 requestedBytes;
+#endif
+#ifdef GC_STATS
+ PRInt32 bin;
+ PRInt64 allocTime;
+#endif
+#ifdef GC_TRACEROOTS
+ PRInt32 traceGeneration;
+#endif
+} GCBlockEnd;
+
+#define PR_BLOCK_END 0xDEADBEEF
+
+/************************************************************************/
+
+#ifdef GC_STATS
+
+typedef struct GCStat {
+ PRInt32 nallocs;
+ double allocTime;
+ double allocTimeVariance;
+ PRInt32 nfrees;
+ double lifetime;
+ double lifetimeVariance;
+} GCStat;
+
+#define GCSTAT_BINS NUM_BINS
+
+GCStat gcstats[GCSTAT_BINS];
+
+#define GCLTFREQ_BINS NUM_BINS
+
+PRInt32 gcltfreq[GCSTAT_BINS][GCLTFREQ_BINS];
+
+#include <math.h>
+
+static char*
+pr_GetSizeString(PRUint32 size)
+{
+ char* sizeStr;
+ if (size < 1024)
+ sizeStr = PR_smprintf("<= %ld", size);
+ else if (size < 1024 * 1024)
+ sizeStr = PR_smprintf("<= %ldk", size / 1024);
+ else
+ sizeStr = PR_smprintf("<= %ldM", size / (1024 * 1024));
+ return sizeStr;
+}
+
+static void
+pr_FreeSizeString(char *sizestr)
+{
+ PR_smprintf_free(sizestr);
+}
+
+
+static void
+pr_PrintGCAllocStats(FILE* out)
+{
+ PRInt32 i, j;
+ _PR_DebugPrint(out, "\n--Allocation-Stats-----------------------------------------------------------");
+ _PR_DebugPrint(out, "\n--Obj-Size----Count-----Avg-Alloc-Time-----------Avg-Lifetime---------%%Freed-\n");
+ for (i = 0; i < GCSTAT_BINS; i++) {
+ GCStat stat = gcstats[i];
+ double allocTimeMean = 0.0, allocTimeVariance = 0.0, lifetimeMean = 0.0, lifetimeVariance = 0.0;
+ PRUint32 maxSize = (1 << i);
+ char* sizeStr;
+ if (stat.nallocs != 0.0) {
+ allocTimeMean = stat.allocTime / stat.nallocs;
+ allocTimeVariance = fabs(stat.allocTimeVariance / stat.nallocs - allocTimeMean * allocTimeMean);
+ }
+ if (stat.nfrees != 0.0) {
+ lifetimeMean = stat.lifetime / stat.nfrees;
+ lifetimeVariance = fabs(stat.lifetimeVariance / stat.nfrees - lifetimeMean * lifetimeMean);
+ }
+ sizeStr = pr_GetSizeString(maxSize);
+ _PR_DebugPrint(out, "%10s %8lu %10.3f +- %10.3f %10.3f +- %10.3f (%2ld%%)\n",
+ sizeStr, stat.nallocs,
+ allocTimeMean, sqrt(allocTimeVariance),
+ lifetimeMean, sqrt(lifetimeVariance),
+ (stat.nallocs ? (stat.nfrees * 100 / stat.nallocs) : 0));
+ pr_FreeSizeString(sizeStr);
+ }
+ _PR_DebugPrint(out, "--Lifetime-Frequency-Counts----------------------------------------------------\n");
+ _PR_DebugPrint(out, "size\\cnt");
+ for (j = 0; j < GCLTFREQ_BINS; j++) {
+ _PR_DebugPrint(out, "\t%lu", j);
+ }
+ _PR_DebugPrint(out, "\n");
+ for (i = 0; i < GCSTAT_BINS; i++) {
+ PRInt32* freqs = gcltfreq[i];
+ _PR_DebugPrint(out, "%lu", (1 << i));
+ for (j = 0; j < GCLTFREQ_BINS; j++) {
+ _PR_DebugPrint(out, "\t%lu", freqs[j]);
+ }
+ _PR_DebugPrint(out, "\n");
+ }
+ _PR_DebugPrint(out, "-------------------------------------------------------------------------------\n");
+}
+
+PR_PUBLIC_API(void)
+PR_PrintGCAllocStats(void)
+{
+ pr_PrintGCAllocStats(stderr);
+}
+
+#endif /* GC_STATS */
+
+/************************************************************************/
+
+/*
+** Sweep a segment, cleaning up all of the debris. Coallese the debris
+** into GCFreeChunk's which are added to the freelist bins.
+*/
+static PRBool SweepSegment(GCSeg *sp)
+{
+ PRWord h, tix;
+ PRWord *p;
+ PRWord *np;
+ PRWord *limit;
+ GCFreeChunk *cp;
+ PRInt32 bytes, chunkSize, segmentSize, totalFree;
+ CollectorType *ct;
+ PRInt32 bin;
+
+ /*
+ ** Now scan over the segment's memory in memory order, coallescing
+ ** all of the debris into a FreeChunk list.
+ */
+ totalFree = 0;
+ segmentSize = sp->limit - sp->base;
+ p = (PRWord *) sp->base;
+ limit = (PRWord *) sp->limit;
+ PR_ASSERT(segmentSize > 0);
+ while (p < limit) {
+ chunkSize = 0;
+ cp = (GCFreeChunk *) p;
+
+ /* Attempt to coallesce any neighboring free objects */
+ for (;;) {
+ PR_ASSERT(IS_HBIT(sp, p) != 0);
+ h = p[0];
+ bytes = OBJ_BYTES(h);
+ PR_ASSERT(bytes != 0);
+ np = (PRWord *) ((char *)p + bytes);
+ tix = (PRWord)GET_TYPEIX(h);
+ if ((h & MARK_BIT) && (tix != FREE_MEMORY_TYPEIX)) {
+#ifdef DEBUG
+ if (tix != FREE_MEMORY_TYPEIX) {
+ PR_ASSERT(_pr_collectorTypes[tix].flags != 0);
+ }
+#endif
+ p[0] = h & ~(MARK_BIT|FINAL_BIT);
+ _GCTRACE(GC_SWEEP, ("busy 0x%x (%d)", p, bytes));
+ break;
+ }
+ _GCTRACE(GC_SWEEP, ("free 0x%x (%d)", p, bytes));
+
+ /* Found a free object */
+#ifdef GC_STATS
+ {
+ PRInt32 userSize = bytes - sizeof(GCBlockEnd);
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + userSize);
+ if (userSize >= 0 && end->check == PR_BLOCK_END) {
+ PRInt64 now = PR_Now();
+ double nowd, delta;
+ PRInt32 freq;
+ LL_L2D(nowd, now);
+ delta = nowd - end->allocTime;
+ gcstats[end->bin].nfrees++;
+ gcstats[end->bin].lifetime += delta;
+ gcstats[end->bin].lifetimeVariance += delta * delta;
+
+ InlineBinNumber(freq, delta);
+ gcltfreq[end->bin][freq]++;
+
+ end->check = 0;
+ }
+ }
+#endif
+ CLEAR_HBIT(sp, p);
+ ct = &_pr_collectorTypes[tix];
+ if (0 != ct->gctype.free) {
+ (*ct->gctype.free)(p + 1);
+ }
+ chunkSize = chunkSize + bytes;
+ if (np == limit) {
+ /* Found the end of heap */
+ break;
+ }
+ PR_ASSERT(np < limit);
+ p = np;
+ }
+
+ if (chunkSize) {
+ _GCTRACE(GC_SWEEP, ("free chunk 0x%p to 0x%p (%d)",
+ cp, (char*)cp + chunkSize - 1, chunkSize));
+ if (chunkSize < MIN_FREE_CHUNK_BYTES) {
+ /* Lost a tiny fragment until (maybe) next time */
+ METER(meter.wastedBytes += chunkSize);
+ p = (PRWord *) cp;
+ chunkSize >>= BYTES_PER_WORD_LOG2;
+ PR_ASSERT(chunkSize != 0);
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, chunkSize);
+ SET_HBIT(sp, p);
+ } else {
+ /* See if the chunk constitutes the entire segment */
+ if (chunkSize == segmentSize) {
+ /* Free up the segment right now */
+ if (sp->info->fromMalloc) {
+ ShrinkGCHeap(sp);
+ return PR_TRUE;
+ }
+ }
+
+ /* Put free chunk into the appropriate bin */
+ cp->segment = sp;
+ cp->chunkSize = chunkSize;
+ InlineBinNumber(bin, chunkSize)
+ cp->next = bins[bin];
+ bins[bin] = cp;
+ if (bin < minBin) minBin = bin;
+ if (bin > maxBin) maxBin = bin;
+
+ /* Zero swept memory now */
+ memset(cp+1, 0, chunkSize - sizeof(*cp));
+ METER(meter.numFreeChunks++);
+ totalFree += chunkSize;
+ }
+ }
+
+ /* Advance to next object */
+ p = np;
+ }
+
+ PR_ASSERT(totalFree <= segmentSize);
+
+ _pr_gcData.freeMemory += totalFree;
+ _pr_gcData.busyMemory += (sp->limit - sp->base) - totalFree;
+ return PR_FALSE;
+}
+
+/************************************************************************/
+
+/* This is a list of all the objects that are finalizable. This is not
+ the list of objects that are awaiting finalization because they
+ have been collected. */
+PRCList _pr_finalizeableObjects;
+
+/* This is the list of objects that are awaiting finalization because
+ they have been collected. */
+PRCList _pr_finalQueue;
+
+/* Each object that requires finalization has one of these objects
+ allocated as well. The GCFinal objects are put on the
+ _pr_finalizeableObjects list until the object is collected at which
+ point the GCFinal object is moved to the _pr_finalQueue */
+typedef struct GCFinalStr {
+ PRCList links;
+ PRWord *object;
+} GCFinal;
+
+/* Find pointer to GCFinal struct from the list linkaged embedded in it */
+#define FinalPtr(_qp) \
+ ((GCFinal*) ((char*) (_qp) - offsetof(GCFinal,links)))
+
+static GCFinal *AllocFinalNode(void)
+{
+ return PR_NEWZAP(GCFinal);
+}
+
+static void FreeFinalNode(GCFinal *node)
+{
+ PR_DELETE(node);
+}
+
+/*
+** Prepare for finalization. At this point in the GC cycle we have
+** identified all of the live objects. For each object on the
+** _pr_finalizeableObjects list see if the object is alive or dead. If
+** it's dead, resurrect it and move it from the _pr_finalizeableObjects
+** list to the _pr_finalQueue (object's only get finalized once).
+**
+** Once _pr_finalizeableObjects has been processed we can finish the
+** GC and free up memory and release the threading lock. After that we
+** can invoke the finalization procs for each object that is on the
+** _pr_finalQueue.
+*/
+static void PrepareFinalize(void)
+{
+ PRCList *qp;
+ GCFinal *fp;
+ PRWord h;
+ PRWord *p;
+ void (PR_CALLBACK *livePointer)(void *ptr);
+#ifdef DEBUG
+ CollectorType *ct;
+#endif
+
+ /* This must be done under the same lock that the finalizer uses */
+ PR_ASSERT( GC_IS_LOCKED() );
+
+ /* cache this ptr */
+ livePointer = _pr_gcData.livePointer;
+
+ /*
+ * Pass #1: Identify objects that are to be finalized, set their
+ * FINAL_BIT.
+ */
+ qp = _pr_finalizeableObjects.next;
+ while (qp != &_pr_finalizeableObjects) {
+ fp = FinalPtr(qp);
+ qp = qp->next;
+ h = fp->object[0]; /* Grab header word */
+ if (h & MARK_BIT) {
+ /* Object is already alive */
+ continue;
+ }
+
+#ifdef DEBUG
+ ct = &_pr_collectorTypes[GET_TYPEIX(h)];
+ PR_ASSERT((0 != ct->flags) && (0 != ct->gctype.finalize));
+#endif
+ fp->object[0] |= FINAL_BIT;
+ _GCTRACE(GC_FINAL, ("moving %p (%d) to finalQueue",
+ fp->object, OBJ_BYTES(h)));
+ }
+
+ /*
+ * Pass #2: For each object that is going to be finalized, move it to
+ * the finalization queue and resurrect it
+ */
+ qp = _pr_finalizeableObjects.next;
+ while (qp != &_pr_finalizeableObjects) {
+ fp = FinalPtr(qp);
+ qp = qp->next;
+ h = fp->object[0]; /* Grab header word */
+ if ((h & FINAL_BIT) == 0) {
+ continue;
+ }
+
+ /* Resurrect the object and any objects it refers to */
+ p = &fp->object[1];
+ (*livePointer)(p);
+ PR_REMOVE_LINK(&fp->links);
+ PR_APPEND_LINK(&fp->links, &_pr_finalQueue);
+ }
+}
+
+/*
+** Scan the finalQ, marking each and every object on it live. This is
+** necessary because we might do a GC before objects that are on the
+** final queue get finalized. Since there are no other references
+** (otherwise they would be on the final queue), we have to scan them.
+** This really only does work if we call the GC before the finalizer
+** has a chance to do its job.
+*/
+extern void PR_CALLBACK _PR_ScanFinalQueue(void *notused)
+{
+ PRCList *qp;
+ GCFinal *fp;
+ PRWord *p;
+ void ( PR_CALLBACK *livePointer)(void *ptr);
+
+ livePointer = _pr_gcData.livePointer;
+ qp = _pr_finalQueue.next;
+ while (qp != &_pr_finalQueue) {
+ fp = FinalPtr(qp);
+ _GCTRACE(GC_FINAL, ("marking 0x%x (on final queue)", fp->object));
+ p = &fp->object[1];
+ (*livePointer)(p);
+ qp = qp->next;
+ }
+}
+
+void PR_CALLBACK FinalizerLoop(void* unused)
+{
+ GCFinal *fp;
+ PRWord *p;
+ PRWord h, tix;
+ CollectorType *ct;
+
+ LOCK_GC();
+ for (;;) {
+ p = 0; h = 0; /* don't let the gc find these pointers */
+ while (PR_CLIST_IS_EMPTY(&_pr_finalQueue))
+ PR_Wait(_pr_gcData.lock, PR_INTERVAL_NO_TIMEOUT);
+
+ _GCTRACE(GC_FINAL, ("begin finalization"));
+ while (_pr_finalQueue.next != &_pr_finalQueue) {
+ fp = FinalPtr(_pr_finalQueue.next);
+ PR_REMOVE_LINK(&fp->links);
+ p = fp->object;
+
+ h = p[0]; /* Grab header word */
+ tix = (PRWord)GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ _GCTRACE(GC_FINAL, ("finalize 0x%x (%d)", p, OBJ_BYTES(h)));
+
+ /*
+ ** Give up the GC lock so that other threads can allocate memory
+ ** while this finalization method is running. Get it back
+ ** afterwards so that the list remains thread safe.
+ */
+ UNLOCK_GC();
+ FreeFinalNode(fp);
+ PR_ASSERT(ct->gctype.finalize != 0);
+ (*ct->gctype.finalize)(p + 1);
+ LOCK_GC();
+ }
+ _GCTRACE(GC_FINAL, ("end finalization"));
+ PR_Notify(_pr_gcData.lock);
+ }
+}
+
+static void NotifyFinalizer(void)
+{
+ if (!PR_CLIST_IS_EMPTY(&_pr_finalQueue)) {
+ PR_ASSERT( GC_IS_LOCKED() );
+ PR_Notify(_pr_gcData.lock);
+ }
+}
+
+void _PR_CreateFinalizer(PRThreadScope scope)
+{
+ if (!_pr_gcData.finalizer) {
+ _pr_gcData.finalizer = PR_CreateThreadGCAble(PR_SYSTEM_THREAD,
+ FinalizerLoop, 0,
+ PR_PRIORITY_LOW, scope,
+ PR_UNJOINABLE_THREAD, 0);
+
+ if (_pr_gcData.finalizer == NULL)
+ /* We are doomed if we can't start the finalizer */
+ PR_Abort();
+
+ }
+}
+
+void pr_FinalizeOnExit(void)
+{
+#ifdef DEBUG_warren
+ OutputDebugString("### Doing finalize-on-exit pass\n");
+#endif
+ PR_ForceFinalize();
+#ifdef DEBUG_warren
+ OutputDebugString("### Finalize-on-exit complete. Dumping object left to memory.out\n");
+ PR_DumpMemorySummary();
+ PR_DumpMemory(PR_TRUE);
+#endif
+}
+
+PR_IMPLEMENT(void) PR_ForceFinalize()
+{
+ LOCK_GC();
+ NotifyFinalizer();
+ while (!PR_CLIST_IS_EMPTY(&_pr_finalQueue)) {
+ PR_ASSERT( GC_IS_LOCKED() );
+ (void) PR_Wait(_pr_gcData.lock, PR_INTERVAL_NO_TIMEOUT);
+ }
+ UNLOCK_GC();
+
+ /* XXX I don't know how to make it wait (yet) */
+}
+
+/************************************************************************/
+
+typedef struct GCWeakStr {
+ PRCList links;
+ PRWord *object;
+} GCWeak;
+
+/*
+** Find pointer to GCWeak struct from the list linkaged embedded in it
+*/
+#define WeakPtr(_qp) \
+ ((GCWeak*) ((char*) (_qp) - offsetof(GCWeak,links)))
+
+PRCList _pr_weakLinks = PR_INIT_STATIC_CLIST(&_pr_weakLinks);
+PRCList _pr_freeWeakLinks = PR_INIT_STATIC_CLIST(&_pr_freeWeakLinks);
+
+#define WEAK_FREELIST_ISEMPTY() (_pr_freeWeakLinks.next == &_pr_freeWeakLinks)
+
+/*
+ * Keep objects referred to by weak free list alive until they can be
+ * freed
+ */
+static void PR_CALLBACK ScanWeakFreeList(void *notused) {
+ PRCList *qp = _pr_freeWeakLinks.next;
+ while (qp != &_pr_freeWeakLinks) {
+ GCWeak *wp = WeakPtr(qp);
+ qp = qp->next;
+ ProcessRootPointer(wp->object);
+ }
+}
+
+/*
+ * Empty the list of weak objects. Note that we can't call malloc/free
+ * under the cover of the GC's lock (we might deadlock), so transfer the
+ * list of free objects to a local list under the cover of the lock, then
+ * release the lock and free up the memory.
+ */
+static void EmptyWeakFreeList(void) {
+ if (!WEAK_FREELIST_ISEMPTY()) {
+ PRCList *qp, freeLinks;
+
+ PR_INIT_CLIST(&freeLinks);
+
+ /*
+ * Transfer list of free weak links from the global list to a
+ * local list.
+ */
+ LOCK_GC();
+ qp = _pr_freeWeakLinks.next;
+ while (qp != &_pr_freeWeakLinks) {
+ GCWeak *wp = WeakPtr(qp);
+ qp = qp->next;
+ PR_REMOVE_LINK(&wp->links);
+ PR_APPEND_LINK(&wp->links, &freeLinks);
+ }
+ UNLOCK_GC();
+
+ /* Free up storage now */
+ qp = freeLinks.next;
+ while (qp != &freeLinks) {
+ GCWeak *wp = WeakPtr(qp);
+ qp = qp->next;
+ PR_DELETE(wp);
+ }
+ }
+}
+
+/*
+ * Allocate a new weak node in the weak objects list
+ */
+static GCWeak *AllocWeakNode(void)
+{
+ EmptyWeakFreeList();
+ return PR_NEWZAP(GCWeak);
+}
+
+static void FreeWeakNode(GCWeak *node)
+{
+ PR_DELETE(node);
+}
+
+/*
+ * Check the weak links for validity. Note that the list of weak links is
+ * itself weak (otherwise we would keep the objects with weak links in
+ * them alive forever). As we scan the list check the weak link object
+ * itself and if it's not marked then remove it from the weak link list
+ */
+static void CheckWeakLinks(void) {
+ PRCList *qp;
+ GCWeak *wp;
+ PRWord *p, h, tix, **weakPtrAddress;
+ CollectorType *ct;
+ PRUint32 offset;
+
+ qp = _pr_weakLinks.next;
+ while (qp != &_pr_weakLinks) {
+ wp = WeakPtr(qp);
+ qp = qp->next;
+ if ((p = wp->object) != 0) {
+ h = p[0]; /* Grab header word */
+ if ((h & MARK_BIT) == 0) {
+ /*
+ * The object that has a weak link is no longer being
+ * referenced; remove it from the chain and let it get
+ * swept away by the GC. Transfer it to the list of
+ * free weak links for later freeing.
+ */
+ PR_REMOVE_LINK(&wp->links);
+ PR_APPEND_LINK(&wp->links, &_pr_freeWeakLinks);
+ collectorCleanupNeeded = 1;
+ continue;
+ }
+
+ /* Examine a live object that contains weak links */
+ tix = GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ PR_ASSERT((ct->flags != 0) && (ct->gctype.getWeakLinkOffset != 0));
+ if (0 == ct->gctype.getWeakLinkOffset) {
+ /* Heap is probably corrupted */
+ continue;
+ }
+
+ /* Get offset into the object of where the weak pointer is */
+ offset = (*ct->gctype.getWeakLinkOffset)(p + 1);
+
+ /* Check the weak pointer */
+ weakPtrAddress = (PRWord**)((char*)(p + 1) + offset);
+ p = *weakPtrAddress;
+ if (p != 0) {
+ h = p[-1]; /* Grab header word for pointed to object */
+ if (h & MARK_BIT) {
+ /* Object can't be dead */
+ continue;
+ }
+ /* Break weak link to an object that is about to be swept */
+ *weakPtrAddress = 0;
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a complete garbage collection
+*/
+
+extern GCLockHook *_pr_GCLockHook;
+
+static void dogc(void)
+{
+ RootFinder *rf;
+ GCLockHook* lhook;
+
+ GCScanQ scanQ;
+ GCSeg *sp, *esp;
+ PRInt64 start, end, diff;
+
+#if defined(GCMETER) || defined(GCTIMINGHOOK)
+ start = PR_Now();
+#endif
+
+ /*
+ ** Stop all of the other threads. This also promises to capture the
+ ** register state of each and every thread
+ */
+
+ /*
+ ** Get all the locks that will be need during GC after SuspendAll. We
+ ** cannot make any locking/library calls after SuspendAll.
+ */
+ if (_pr_GCLockHook) {
+ for (lhook = _pr_GCLockHook->next; lhook != _pr_GCLockHook;
+ lhook = lhook->next) {
+ (*lhook->func)(PR_GCBEGIN, lhook->arg);
+ }
+ }
+
+ PR_SuspendAll();
+
+#ifdef GCMETER
+ /* Reset meter info */
+ if (_pr_gcMeter & _GC_METER_STATS) {
+ fprintf(stderr,
+ "[GCSTATS: busy:%ld skipped:%ld, alloced:%ld+wasted:%ld+free:%ld = total:%ld]\n",
+ (long) _pr_gcData.busyMemory,
+ (long) meter.skippedFreeChunks,
+ (long) meter.allocBytes,
+ (long) meter.wastedBytes,
+ (long) _pr_gcData.freeMemory,
+ (long) _pr_gcData.allocMemory);
+ }
+ memset(&meter, 0, sizeof(meter));
+#endif
+
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("begin mark phase; busy=%d free=%d total=%d",
+ _pr_gcData.busyMemory, _pr_gcData.freeMemory,
+ _pr_gcData.allocMemory));
+
+ if (_pr_beginGCHook) {
+ (*_pr_beginGCHook)(_pr_beginGCHookArg);
+ }
+
+ /*
+ ** Initialize scanQ to all zero's so that root finder doesn't walk
+ ** over it...
+ */
+ memset(&scanQ, 0, sizeof(scanQ));
+ pScanQ = &scanQ;
+
+ /******************************************/
+ /* MARK PHASE */
+
+ EmptyFreelists();
+
+ /* Find root's */
+ PR_LOG(_pr_msgc_lm, PR_LOG_WARNING,
+ ("begin mark phase; busy=%d free=%d total=%d",
+ _pr_gcData.busyMemory, _pr_gcData.freeMemory,
+ _pr_gcData.allocMemory));
+ METER(_pr_scanDepth = 0);
+ rf = _pr_rootFinders;
+ while (rf) {
+ _GCTRACE(GC_ROOTS, ("finding roots in %s", rf->name));
+ (*rf->func)(rf->arg);
+ rf = rf->next;
+ }
+ _GCTRACE(GC_ROOTS, ("done finding roots"));
+
+ /* Scan remaining object's that need scanning */
+ ScanScanQ(&scanQ);
+ PR_ASSERT(pScanQ == &scanQ);
+ PR_ASSERT(scanQ.queued == 0);
+ METER({
+ if (_pr_scanDepth > _pr_maxScanDepth) {
+ _pr_maxScanDepth = _pr_scanDepth;
+ }
+ });
+
+ /******************************************/
+ /* FINALIZATION PHASE */
+
+ METER(_pr_scanDepth = 0);
+ PrepareFinalize();
+
+ /* Scan any resurrected objects found during finalization */
+ ScanScanQ(&scanQ);
+ PR_ASSERT(pScanQ == &scanQ);
+ PR_ASSERT(scanQ.queued == 0);
+ METER({
+ if (_pr_scanDepth > _pr_maxScanDepth) {
+ _pr_maxScanDepth = _pr_scanDepth;
+ }
+ });
+ pScanQ = 0;
+
+ /******************************************/
+ /* SWEEP PHASE */
+
+ /*
+ ** Sweep each segment clean. While we are at it, figure out which
+ ** segment has the most free space and make that the current segment.
+ */
+ CheckWeakLinks();
+ _GCTRACE(GC_SWEEP, ("begin sweep phase"));
+ _pr_gcData.freeMemory = 0;
+ _pr_gcData.busyMemory = 0;
+ sp = segs;
+ esp = sp + nsegs;
+ while (sp < esp) {
+ if (SweepSegment(sp)) {
+ /*
+ ** Segment is now free and has been replaced with a different
+ ** segment object.
+ */
+ esp--;
+ continue;
+ }
+ sp++;
+ }
+
+#if defined(GCMETER) || defined(GCTIMINGHOOK)
+ end = PR_Now();
+#endif
+#ifdef GCMETER
+ LL_SUB(diff, end, start);
+ PR_LOG(GC, PR_LOG_ALWAYS,
+ ("done; busy=%d free=%d chunks=%d total=%d time=%lldms",
+ _pr_gcData.busyMemory, _pr_gcData.freeMemory,
+ meter.numFreeChunks, _pr_gcData.allocMemory, diff));
+ if (_pr_gcMeter & _GC_METER_FREE_LIST) {
+ PRIntn bin;
+ fprintf(stderr, "Freelist bins:\n");
+ for (bin = 0; bin < NUM_BINS; bin++) {
+ GCFreeChunk *cp = bins[bin];
+ while (cp != NULL) {
+ fprintf(stderr, "%3d: %p %8ld\n",
+ bin, cp, (long) cp->chunkSize);
+ cp = cp->next;
+ }
+ }
+ }
+#endif
+
+ if (_pr_endGCHook) {
+ (*_pr_endGCHook)(_pr_endGCHookArg);
+ }
+
+ /* clear the running total of the bytes allocated via BigAlloc() */
+ bigAllocBytes = 0;
+
+ /* And resume multi-threading */
+ PR_ResumeAll();
+
+ if (_pr_GCLockHook) {
+ for (lhook = _pr_GCLockHook->prev; lhook != _pr_GCLockHook;
+ lhook = lhook->prev) {
+ (*lhook->func)(PR_GCEND, lhook->arg);
+ }
+ }
+
+ /* Kick finalizer */
+ NotifyFinalizer();
+#ifdef GCTIMINGHOOK
+ if (_pr_gcData.gcTimingHook) {
+ PRInt32 time;
+ LL_SUB(diff, end, start);
+ LL_L2I(time, diff);
+ _pr_gcData.gcTimingHook(time);
+ }
+#endif
+}
+
+PR_IMPLEMENT(void) PR_GC(void)
+{
+ LOCK_GC();
+ dogc();
+ UNLOCK_GC();
+
+ EmptyWeakFreeList();
+}
+
+/*******************************************************************************
+ * Heap Walker
+ ******************************************************************************/
+
+/*
+** This is yet another disgusting copy of the body of ProcessRootPointer
+** (the other being ProcessRootBlock), but we're not leveraging a single
+** function in their cases in interest of performance (avoiding the function
+** call).
+*/
+static PRInt32 PR_CALLBACK
+pr_ConservativeWalkPointer(void* ptr, PRWalkFun walkRootPointer, void* data)
+{
+ PRWord *p0, *p, *segBase;
+ GCSeg* sp;
+
+ p0 = (PRWord*) ptr;
+
+ if (p0 < _pr_gcData.lowSeg) return 0; /* below gc heap */
+ if (p0 >= _pr_gcData.highSeg) return 0; /* above gc heap */
+
+ /* NOTE: inline expansion of InHeap */
+ /* Find segment */
+ sp = lastInHeap;
+ if (!sp || !IN_SEGMENT(sp,p0)) {
+ GCSeg *esp;
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p0)) {
+ lastInHeap = sp;
+ goto find_object;
+ }
+ }
+ return 0;
+ }
+
+ find_object:
+ /* NOTE: Inline expansion of FindObject */
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p0 & ~(BYTES_PER_WORD-1L));
+ segBase = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ goto winner;
+ }
+ p--;
+ } while (p >= segBase);
+
+ /*
+ ** We have a pointer into the heap, but it has no header
+ ** bit. This means that somehow the very first object in the heap
+ ** doesn't have a header. This is impossible so when debugging
+ ** lets abort.
+ */
+#ifdef DEBUG
+ PR_Abort();
+#endif
+ return 0;
+
+ winner:
+ return walkRootPointer(p, data);
+}
+
+static PRInt32 PR_CALLBACK
+pr_ConservativeWalkBlock(void **base, PRInt32 count,
+ PRWalkFun walkRootPointer, void* data)
+{
+ PRWord *p0;
+ while (--count >= 0) {
+ PRInt32 status;
+ p0 = (PRWord*) *base++;
+ status = pr_ConservativeWalkPointer(p0, walkRootPointer, data);
+ if (status) return status;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+
+typedef void (*WalkObject_t)(FILE *out, GCType* tp, PRWord *obj,
+ size_t bytes, PRBool detailed);
+typedef void (*WalkUnknown_t)(FILE *out, GCType* tp, PRWord tix, PRWord *p,
+ size_t bytes, PRBool detailed);
+typedef void (*WalkFree_t)(FILE *out, PRWord *p, size_t size, PRBool detailed);
+typedef void (*WalkSegment_t)(FILE *out, GCSeg* sp, PRBool detailed);
+
+static void
+pr_WalkSegment(FILE* out, GCSeg* sp, PRBool detailed,
+ char* enterMsg, char* exitMsg,
+ WalkObject_t walkObject, WalkUnknown_t walkUnknown, WalkFree_t walkFree)
+{
+ PRWord *p, *limit;
+
+ p = (PRWord *) sp->base;
+ limit = (PRWord *) sp->limit;
+ if (enterMsg)
+ fprintf(out, enterMsg, p);
+ while (p < limit)
+ {
+ if (IS_HBIT(sp, p)) /* Is this an object header? */
+ {
+ PRWord h = p[0];
+ PRWord tix = GET_TYPEIX(h);
+ size_t bytes = OBJ_BYTES(h);
+ PRWord* np = (PRWord*) ((char*)p + bytes);
+
+ GCType* tp = &_pr_collectorTypes[tix].gctype;
+ if ((0 != tp) && walkObject)
+ walkObject(out, tp, p, bytes, detailed);
+ else if (walkUnknown)
+ walkUnknown(out, tp, tix, p, bytes, detailed);
+ p = np;
+ }
+ else
+ {
+ /* Must be a freelist item */
+ size_t size = ((GCFreeChunk*)p)->chunkSize;
+ if (walkFree)
+ walkFree(out, p, size, detailed);
+ p = (PRWord*)((char*)p + size);
+ }
+ }
+ if (p != limit)
+ fprintf(out, "SEGMENT OVERRUN (end should be at 0x%p)\n", limit);
+ if (exitMsg)
+ fprintf(out, exitMsg, p);
+}
+
+static void
+pr_WalkSegments(FILE *out, WalkSegment_t walkSegment, PRBool detailed)
+{
+ GCSeg *sp = segs;
+ GCSeg *esp;
+
+ LOCK_GC();
+ esp = sp + nsegs;
+ while (sp < esp)
+ {
+ walkSegment(out, sp, detailed);
+ sp++;
+ }
+ fprintf(out, "End of heap\n");
+ UNLOCK_GC();
+}
+
+/*******************************************************************************
+ * Heap Dumper
+ ******************************************************************************/
+
+PR_IMPLEMENT(void)
+PR_DumpIndent(FILE *out, int indent)
+{
+ while (--indent >= 0)
+ fprintf(out, " ");
+}
+
+static void
+PR_DumpHexWords(FILE *out, PRWord *p, int nWords,
+ int indent, int nWordsPerLine)
+{
+ while (nWords > 0)
+ {
+ int i;
+
+ PR_DumpIndent(out, indent);
+ i = nWordsPerLine;
+ if (i > nWords)
+ i = nWords;
+ nWords -= i;
+ while (i--)
+ {
+ fprintf(out, "0x%.8lX", (long) *p++);
+ if (i)
+ fputc(' ', out);
+ }
+ fputc('\n', out);
+ }
+}
+
+static void PR_CALLBACK
+pr_DumpObject(FILE *out, GCType* tp, PRWord *p,
+ size_t bytes, PRBool detailed)
+{
+ char kindChar = tp->kindChar;
+ fprintf(out, "0x%p: 0x%.6lX %c ",
+ p, (long) bytes, kindChar ? kindChar : '?');
+ if (tp->dump)
+ (*tp->dump)(out, (void*) (p + 1), detailed, 0);
+ if (detailed)
+ PR_DumpHexWords(out, p, bytes>>2, 22, 4);
+}
+
+static void PR_CALLBACK
+pr_DumpUnknown(FILE *out, GCType* tp, PRWord tix, PRWord *p,
+ size_t bytes, PRBool detailed)
+{
+ char kindChar = tp->kindChar;
+ fprintf(out, "0x%p: 0x%.6lX %c ",
+ p, (long) bytes, kindChar ? kindChar : '?');
+ fprintf(out, "UNKNOWN KIND %ld\n", (long) tix);
+ if (detailed)
+ PR_DumpHexWords(out, p, bytes>>2, 22, 4);
+}
+
+static void PR_CALLBACK
+pr_DumpFree(FILE *out, PRWord *p, size_t size, PRBool detailed)
+{
+ fprintf(out, "0x%p: 0x%.6lX - FREE\n", p, (long) size);
+}
+
+static void PR_CALLBACK
+pr_DumpSegment(FILE* out, GCSeg* sp, PRBool detailed)
+{
+ pr_WalkSegment(out, sp, detailed,
+ "\n Address: Length\n0x%p: Beginning of segment\n",
+ "0x%p: End of segment\n\n",
+ pr_DumpObject, pr_DumpUnknown, pr_DumpFree);
+}
+
+static void pr_DumpRoots(FILE *out);
+
+/*
+** Dump out the GC heap.
+*/
+PR_IMPLEMENT(void)
+PR_DumpGCHeap(FILE *out, PRBool detailed)
+{
+ fprintf(out, "\n"
+ "The kinds are:\n"
+ " U unscanned block\n"
+ " W weak link block\n"
+ " S scanned block\n"
+ " F scanned and final block\n"
+ " C class record\n"
+ " X context record\n"
+ " - free list item\n"
+ " ? other\n");
+ LOCK_GC();
+ pr_WalkSegments(out, pr_DumpSegment, detailed);
+ if (detailed)
+ pr_DumpRoots(out);
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void)
+PR_DumpMemory(PRBool detailed)
+{
+ PR_DumpToFile("memory.out", "Dumping memory", PR_DumpGCHeap, detailed);
+}
+
+/******************************************************************************/
+
+static PRInt32 PR_CALLBACK
+pr_DumpRootPointer(PRWord* p, void* data)
+{
+ PRWord h = p[0];
+ PRWord tix = GET_TYPEIX(h);
+ size_t bytes = OBJ_BYTES(h);
+
+ GCType* tp = &_pr_collectorTypes[tix].gctype;
+ if (0 != tp)
+ pr_DumpObject(_pr_gcData.dumpOutput, tp, p, bytes, PR_FALSE);
+ else
+ pr_DumpUnknown(_pr_gcData.dumpOutput, tp, tix, p, bytes, PR_FALSE);
+ return 0;
+}
+
+static void PR_CALLBACK
+pr_ConservativeDumpRootPointer(void* ptr)
+{
+ (void)pr_ConservativeWalkPointer(ptr, (PRWalkFun) pr_DumpRootPointer, NULL);
+}
+
+static void PR_CALLBACK
+pr_ConservativeDumpRootBlock(void **base, PRInt32 count)
+{
+ (void)pr_ConservativeWalkBlock(base, count, (PRWalkFun) pr_DumpRootPointer, NULL);
+}
+
+extern int
+DumpThreadRoots(PRThread *t, int i, void *notused);
+
+static void
+pr_DumpRoots(FILE *out)
+{
+ RootFinder *rf;
+ void (*liveBlock)(void **base, PRInt32 count);
+ void (*livePointer)(void *ptr);
+ void (*processRootBlock)(void **base, PRInt32 count);
+ void (*processRootPointer)(void *ptr);
+
+ LOCK_GC();
+
+ liveBlock = _pr_gcData.liveBlock;
+ livePointer = _pr_gcData.livePointer;
+ processRootBlock = _pr_gcData.processRootBlock;
+ processRootPointer = _pr_gcData.processRootPointer;
+
+ _pr_gcData.liveBlock = pr_ConservativeDumpRootBlock;
+ _pr_gcData.livePointer = pr_ConservativeDumpRootPointer;
+ _pr_gcData.processRootBlock = pr_ConservativeDumpRootBlock;
+ _pr_gcData.processRootPointer = pr_ConservativeDumpRootPointer;
+ _pr_gcData.dumpOutput = out;
+
+ rf = _pr_rootFinders;
+ while (rf) {
+ fprintf(out, "\n===== Roots for %s\n", rf->name);
+ (*rf->func)(rf->arg);
+ rf = rf->next;
+ }
+
+ _pr_gcData.liveBlock = liveBlock;
+ _pr_gcData.livePointer = livePointer;
+ _pr_gcData.processRootBlock = processRootBlock;
+ _pr_gcData.processRootPointer = processRootPointer;
+ _pr_gcData.dumpOutput = NULL;
+
+ UNLOCK_GC();
+}
+
+/*******************************************************************************
+ * Heap Summary Dumper
+ ******************************************************************************/
+
+PRSummaryPrinter summaryPrinter = NULL;
+void* summaryPrinterClosure = NULL;
+
+PR_IMPLEMENT(void)
+PR_RegisterSummaryPrinter(PRSummaryPrinter fun, void* closure)
+{
+ summaryPrinter = fun;
+ summaryPrinterClosure = closure;
+}
+
+static void PR_CALLBACK
+pr_SummarizeObject(FILE *out, GCType* tp, PRWord *p,
+ size_t bytes, PRBool detailed)
+{
+ if (tp->summarize)
+ (*tp->summarize)((void GCPTR*)(p + 1), bytes);
+}
+
+static void PR_CALLBACK
+pr_DumpSummary(FILE* out, GCSeg* sp, PRBool detailed)
+{
+ pr_WalkSegment(out, sp, detailed, NULL, NULL,
+ pr_SummarizeObject, NULL, NULL);
+}
+
+PR_IMPLEMENT(void)
+PR_DumpGCSummary(FILE *out, PRBool detailed)
+{
+ if (summaryPrinter) {
+ pr_WalkSegments(out, pr_DumpSummary, detailed);
+ summaryPrinter(out, summaryPrinterClosure);
+ }
+#if 0
+ fprintf(out, "\nFinalizable objects:\n");
+ {
+ PRCList *qp;
+ qp = _pr_pendingFinalQueue.next;
+ while (qp != &_pr_pendingFinalQueue) {
+ GCFinal* fp = FinalPtr(qp);
+ PRWord h = fp->object[0]; /* Grab header word */
+ PRWord tix = GET_TYPEIX(h);
+ GCType* tp = _pr_gcTypes[tix];
+ size_t bytes = OBJ_BYTES(h);
+ pr_DumpObject(out, tp, fp->object, bytes, PR_FALSE);
+ qp = qp->next;
+ }
+ }
+#endif
+}
+
+PR_IMPLEMENT(void)
+PR_DumpMemorySummary(void)
+{
+ PR_DumpToFile("memory.out", "Memory Summary", PR_DumpGCSummary, PR_FALSE);
+}
+
+/*******************************************************************************
+ * End Of Heap Walker
+ ******************************************************************************/
+
+#ifdef GC_TRACEROOTS
+
+PRInt32 pr_traceGen = 0;
+
+static PRBool
+pr_IsMarked(PRWord* p)
+{
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ PR_ASSERT(end->check == PR_BLOCK_END);
+ return end->traceGeneration == pr_traceGen;
+}
+
+static void
+pr_Mark(PRWord* p)
+{
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ PR_ASSERT(end->check == PR_BLOCK_END);
+ end->traceGeneration = pr_traceGen;
+}
+
+PRWord* pr_traceObj; /* set this in the debugger, then execute PR_TraceRoot() */
+
+static PRInt32 PR_CALLBACK
+pr_TraceRootObject(void* obj, void* data);
+
+static PRInt32 PR_CALLBACK
+pr_TraceRootPointer(PRWord *p, void* data)
+{
+ PRInt32 printTrace = 0;
+ PRWord h = p[0];
+ PRWord tix = GET_TYPEIX(h);
+ GCType* tp = &_pr_collectorTypes[tix].gctype;
+ FILE* out = _pr_gcData.dumpOutput;
+
+ PR_ASSERT(tp);
+ if (pr_IsMarked(p))
+ return printTrace;
+
+ pr_Mark(p);
+ if (p == pr_traceObj) {
+ fprintf(out, "\n### Found path to:\n");
+ printTrace = 1;
+ }
+ else {
+ if (PR_StackSpaceLeft(PR_GetCurrentThread()) < 512) {
+ fprintf(out, "\n### Path too deep (giving up):\n");
+ printTrace = 1;
+ }
+ else if (tp->walk) {
+ printTrace = tp->walk((void*)(p + 1), pr_TraceRootObject, data);
+ }
+ /* else there's no way to walk this object, so we
+ haven't found what we're looking for */
+ }
+
+ if (printTrace == 1) {
+ PR_ASSERT(tp->dump);
+ fprintf(out, "0x%p: ", p);
+ tp->dump(out, (void*)(p + 1), PR_FALSE, 1);
+ }
+ return printTrace;
+}
+
+static PRInt32 PR_CALLBACK
+pr_TraceRootObject(void* obj, void* data)
+{
+ /* This version of pr_TraceRootPointer takes object
+ pointers, instead of gc header pointers. */
+ return pr_TraceRootPointer((PRWord*)obj - 1, data);
+}
+
+static void PR_CALLBACK
+pr_ConservativeTraceRootPointer(PRWord *p)
+{
+ PRInt32 status;
+ ++pr_traceGen;
+ status = pr_ConservativeWalkPointer(p, pr_TraceRootPointer, NULL);
+ if (status) {
+ FILE* out = _pr_gcData.dumpOutput;
+ fprintf(out, "### from root at 0x%p\n\n", p);
+ }
+}
+
+static void PR_CALLBACK
+pr_ConservativeTraceRootBlock(void **base, PRInt32 count)
+{
+ PRInt32 status;
+ ++pr_traceGen;
+ status = pr_ConservativeWalkBlock(base, count, pr_TraceRootPointer, NULL);
+ if (status) {
+ FILE* out = _pr_gcData.dumpOutput;
+ fprintf(out, "### from root in range 0x%p + 0x%lx\n\n",
+ base, (long) count);
+ }
+}
+
+static void
+PR_TraceRoot1(FILE* out, PRBool detailed)
+{
+ RootFinder *rf;
+ void (*liveBlock)(void **base, PRInt32 count);
+ void (*livePointer)(void *ptr);
+ void (*processRootBlock)(void **base, PRInt32 count);
+ void (*processRootPointer)(void *ptr);
+
+ LOCK_GC();
+
+ liveBlock = _pr_gcData.liveBlock;
+ livePointer = _pr_gcData.livePointer;
+ processRootBlock = _pr_gcData.processRootBlock;
+ processRootPointer = _pr_gcData.processRootPointer;
+
+ _pr_gcData.liveBlock = pr_ConservativeTraceRootBlock;
+ _pr_gcData.livePointer = pr_ConservativeTraceRootPointer;
+ _pr_gcData.processRootBlock = pr_ConservativeTraceRootBlock;
+ _pr_gcData.processRootPointer = pr_ConservativeTraceRootPointer;
+ _pr_gcData.dumpOutput = out;
+
+ fprintf(out, "### Looking for paths to 0x%p\n\n", pr_traceObj);
+
+ rf = _pr_rootFinders;
+ while (rf) {
+ fprintf(out, "\n===== Roots for %s\n", rf->name);
+ (*rf->func)(rf->arg);
+ rf = rf->next;
+ }
+
+ _pr_gcData.liveBlock = liveBlock;
+ _pr_gcData.livePointer = livePointer;
+ _pr_gcData.processRootBlock = processRootBlock;
+ _pr_gcData.processRootPointer = processRootPointer;
+ _pr_gcData.dumpOutput = NULL;
+
+ UNLOCK_GC();
+}
+
+PR_PUBLIC_API(void)
+PR_TraceRoot()
+{
+ /*
+ ** How this works:
+ ** Once you find the object you want to trace the roots of, set the
+ ** global variable pr_traceObj to point to it (the header, not the
+ ** java handle), and then call this routine (on Windows, you can set
+ ** a breakpoint at the end of a function that returns void (e.g. dogc)
+ ** and then do a "set next statement" to point to this routine and go.
+ ** This will dump a list of the paths from the roots to the object in
+ ** question to your memory.out file.
+ */
+ PR_DumpToFile("memory.out", "Tracing Roots", PR_TraceRoot1, PR_FALSE);
+}
+
+#endif /* GC_TRACEROOTS */
+
+/******************************************************************************/
+
+#if defined(DEBUG) && defined(WIN32)
+static void DumpApplicationHeap(FILE *out, HANDLE heap)
+{
+ PROCESS_HEAP_ENTRY entry;
+ DWORD err;
+
+ if (!HeapLock(heap))
+ OutputDebugString("Can't lock the heap.\n");
+ entry.lpData = 0;
+ fprintf(out, " address: size ovhd region\n");
+ while (HeapWalk(heap, &entry))
+ {
+ WORD flags = entry.wFlags;
+
+ fprintf(out, "0x%.8X: 0x%.8X 0x%.2X 0x%.2X ", entry.lpData, entry.cbData,
+ entry.cbOverhead, entry.iRegionIndex);
+ if (flags & PROCESS_HEAP_REGION)
+ fprintf(out, "REGION committedSize=0x%.8X uncommittedSize=0x%.8X firstBlock=0x%.8X lastBlock=0x%.8X",
+ entry.Region.dwCommittedSize, entry.Region.dwUnCommittedSize,
+ entry.Region.lpFirstBlock, entry.Region.lpLastBlock);
+ else if (flags & PROCESS_HEAP_UNCOMMITTED_RANGE)
+ fprintf(out, "UNCOMMITTED");
+ else if (flags & PROCESS_HEAP_ENTRY_BUSY)
+ {
+ if (flags & PROCESS_HEAP_ENTRY_DDESHARE)
+ fprintf(out, "DDEShare ");
+ if (flags & PROCESS_HEAP_ENTRY_MOVEABLE)
+ fprintf(out, "Moveable Block handle=0x%.8X", entry.Block.hMem);
+ else
+ fprintf(out, "Block");
+ }
+ fprintf(out, "\n");
+ }
+ if ((err = GetLastError()) != ERROR_NO_MORE_ITEMS)
+ fprintf(out, "ERROR %d iterating through the heap\n", err);
+ if (!HeapUnlock(heap))
+ OutputDebugString("Can't unlock the heap.\n");
+}
+#endif
+
+#if defined(DEBUG) && defined(WIN32)
+static void DumpApplicationHeaps(FILE *out)
+{
+ HANDLE mainHeap;
+ HANDLE heaps[100];
+ DWORD nHeaps;
+ PRInt32 i;
+
+ mainHeap = GetProcessHeap();
+ nHeaps = GetProcessHeaps(100, heaps);
+ if (nHeaps > 100)
+ nHeaps = 0;
+ fprintf(out, "%ld heaps:\n", (long) nHeaps);
+ for (i = 0; i<nHeaps; i++)
+ {
+ HANDLE heap = heaps[i];
+
+ fprintf(out, "Heap at 0x%.8lX", (long) heap);
+ if (heap == mainHeap)
+ fprintf(out, " (main)");
+ fprintf(out, ":\n");
+ DumpApplicationHeap(out, heap);
+ fprintf(out, "\n");
+ }
+ fprintf(out, "End of heap dump\n\n");
+}
+#endif
+
+#if defined(DEBUG) && defined(WIN32)
+PR_IMPLEMENT(void) PR_DumpApplicationHeaps(void)
+{
+ FILE *out;
+
+ OutputDebugString("Dumping heaps...");
+ out = fopen("heaps.out", "a");
+ if (!out)
+ OutputDebugString("Can't open \"heaps.out\"\n");
+ else
+ {
+ struct tm *newtime;
+ time_t aclock;
+
+ time(&aclock);
+ newtime = localtime(&aclock);
+ fprintf(out, "Heap dump on %s\n", asctime(newtime)); /* Print current time */
+ DumpApplicationHeaps(out);
+ fprintf(out, "\n\n");
+ fclose(out);
+ }
+ OutputDebugString(" done\n");
+}
+#else
+
+PR_IMPLEMENT(void) PR_DumpApplicationHeaps(void)
+{
+ fprintf(stderr, "Native heap dumping is currently implemented only for Windows32.\n");
+}
+#endif
+
+/************************************************************************/
+
+/*
+** Scan the freelist bins looking for a big enough chunk of memory to
+** hold "bytes" worth of allocation. "bytes" already has the
+** per-allocation header added to it. Return a pointer to the object with
+** its per-allocation header already prepared.
+*/
+static PRWord *BinAlloc(int cbix, PRInt32 bytes, int dub)
+{
+ GCFreeChunk **cpp, *cp, *cpNext;
+ GCSeg *sp;
+ PRInt32 chunkSize, remainder;
+ PRWord *p, *np;
+ PRInt32 bin, newbin;
+
+ /* Compute bin that allocation belongs in */
+ InlineBinNumber(bin,bytes)
+ if (bin < minBin) {
+ bin = minBin; /* Start at first filled bin */
+ }
+
+ /* Search in the bin, and larger bins, for a big enough piece */
+ for (; bin <= NUM_BINS-1; bin++) {
+ cpp = &bins[bin];
+ while ((cp = *cpp) != 0) {
+ chunkSize = cp->chunkSize;
+ if (chunkSize < bytes) {
+ /* Too small; skip it */
+ METER(meter.skippedFreeChunks++);
+ cpp = &cp->next;
+ continue;
+ }
+
+ /* We have found a hunk of memory large enough to use */
+ p = (PRWord*) cp;
+ sp = cp->segment;
+ cpNext = cp->next;
+#ifndef IS_64
+ if (dub && (((PRWord)p & (PR_BYTES_PER_DWORD-1)) == 0)) {
+ /*
+ * We are double aligning the memory and the current free
+ * chunk is aligned on an even boundary. Because header
+ * words are one word long we need to discard the first
+ * word of memory.
+ */
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, 1);
+ SET_HBIT(sp, p);
+ p++;
+ chunkSize -= PR_BYTES_PER_WORD;
+ bytes -= PR_BYTES_PER_WORD;
+ PR_ASSERT(((PRWord)p & (PR_BYTES_PER_DWORD-1)) != 0);
+ _pr_gcData.freeMemory -= PR_BYTES_PER_WORD;
+ _pr_gcData.busyMemory += PR_BYTES_PER_WORD;
+ }
+#endif
+ np = (PRWord*) ((char*) p + bytes);
+ remainder = chunkSize - bytes;
+ if (remainder >= MIN_FREE_CHUNK_BYTES) {
+ /* The left over memory is large enough to be freed. */
+ cp = (GCFreeChunk*) np;
+ cp->segment = sp;
+ cp->chunkSize = remainder;
+ InlineBinNumber(newbin, remainder)
+ if (newbin != bin) {
+ *cpp = (GCFreeChunk*) cpNext; /* remove */
+ cp->next = bins[newbin]; /* insert */
+ bins[newbin] = cp;
+ if (newbin < minBin) minBin = newbin;
+ if (newbin > maxBin) maxBin = newbin;
+ } else {
+ /* Leave it on the same list */
+ cp->next = cpNext;
+ *cpp = (GCFreeChunk*) np;
+ }
+ } else {
+ /*
+ * The left over memory is too small to be released. Just
+ * leave it attached to the chunk of memory being
+ * returned.
+ */
+ *cpp = cpNext;
+ bytes = chunkSize;
+ }
+ p[0] = MAKE_HEADER(cbix, (bytes >> PR_BYTES_PER_WORD_LOG2));
+ SET_HBIT(sp, p);
+ _pr_gcData.freeMemory -= bytes;
+ _pr_gcData.busyMemory += bytes;
+ return p;
+ }
+ }
+ return 0;
+}
+
+/*
+** Allocate a piece of memory that is "big" in it's own segment. Make
+** the object consume the entire segment to avoid fragmentation. When
+** the object is no longer referenced, the segment is freed.
+*/
+static PRWord *BigAlloc(int cbix, PRInt32 bytes, int dub)
+{
+ GCSeg *sp;
+ PRWord *p, h;
+ PRInt32 chunkSize;
+
+ /*
+ ** If the number of bytes allocated via BigAlloc() since the last GC
+ ** exceeds BIG_ALLOC_GC_SIZE then do a GC Now...
+ */
+ if (bigAllocBytes >= BIG_ALLOC_GC_SIZE) {
+ dogc();
+ }
+ bigAllocBytes += bytes;
+
+ /* Get a segment to hold this allocation */
+ sp = GrowHeapExactly(bytes);
+
+ if (sp) {
+ p = (PRWord*) sp->base;
+ chunkSize = sp->limit - sp->base;
+
+ /* All memory is double aligned on 64 bit machines... */
+#ifndef IS_64
+ if (dub && (((PRWord)p & (PR_BYTES_PER_DWORD-1)) == 0)) {
+ /*
+ ** Consume the first word of the chunk with a dummy
+ ** unreferenced object.
+ */
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, 1);
+ SET_HBIT(sp, p);
+ p++;
+ chunkSize -= PR_BYTES_PER_WORD;
+ _pr_gcData.freeMemory -= PR_BYTES_PER_WORD;
+ _pr_gcData.busyMemory += PR_BYTES_PER_WORD;
+ PR_ASSERT(((PRWord)p & (PR_BYTES_PER_DWORD-1)) != 0);
+ }
+#endif
+
+ /* Consume the *entire* segment with a single allocation */
+ h = MAKE_HEADER(cbix, (chunkSize >> PR_BYTES_PER_WORD_LOG2));
+ p[0] = h;
+ SET_HBIT(sp, p);
+ _pr_gcData.freeMemory -= chunkSize;
+ _pr_gcData.busyMemory += chunkSize;
+ return p;
+ }
+ return 0;
+}
+
+/* we disable gc allocation during low memory conditions */
+static PRBool allocationEnabled = PR_TRUE;
+
+PR_IMPLEMENT(void) PR_EnableAllocation(PRBool yesOrNo)
+{
+ allocationEnabled = yesOrNo;
+}
+
+static void CollectorCleanup(void) {
+ while (collectorCleanupNeeded) {
+ LOCK_GC();
+ collectorCleanupNeeded = 0;
+ UNLOCK_GC();
+ if (freeSegs) {
+ FreeSegments();
+ }
+ if (!WEAK_FREELIST_ISEMPTY()) {
+ EmptyWeakFreeList();
+ }
+ }
+}
+
+/******************************************************************************/
+
+#ifdef GC_CHECK
+static PRInt32 allocationCount;
+
+static void EarthShatteringKaBoom(PRInt32 whichOne) {
+ long* p = 0;
+ *p = 0;
+}
+
+/* Check a segment of heap memory. Verify that the object memory
+ hasn't been overwritten (past the end at least) */
+static void CheckSegment(GCSeg* sp) {
+ PRWord h, tix;
+ PRWord *p, *lastp, *np, *limit;
+
+ lastp = p = (PRWord *) sp->base;
+ limit = (PRWord *) sp->limit;
+ while (p < limit) {
+ if (IS_HBIT(sp, p)) {
+ char *cp, i;
+ GCBlockEnd* end;
+ PRWord bytes, requestedBytes;
+
+ h = p[0];
+ tix = GET_TYPEIX(h);
+ bytes = OBJ_BYTES(h);
+ np = (PRWord *) ((char *)p + bytes);
+ if (tix != FREE_MEMORY_TYPEIX) {
+ PRInt32 test; /* msdev get's fooled without this local */
+ /* A live object is here. The last word in the object will
+ contain the objects requestedSize */
+ end = (GCBlockEnd*)((char*)(p) + bytes - sizeof(GCBlockEnd));
+ test = end->check;
+ if (test != PR_BLOCK_END) {
+ PR_ASSERT(test == PR_BLOCK_END);
+ }
+ requestedBytes = end->requestedBytes;
+ if (requestedBytes >= bytes) EarthShatteringKaBoom(0);
+ cp = (char*)(p + 1) + requestedBytes;
+ i = (char) 0xff;
+ while (cp < (char*)end) {
+ if (*cp != i) EarthShatteringKaBoom(1);
+ cp++;
+ i--;
+ }
+ }
+ lastp = p;
+ p = np;
+ } else {
+ /* Must be a freelist item */
+ GCFreeChunk *cp = (GCFreeChunk*) p;
+ if ((PRInt32)cp->chunkSize < (PRInt32)sizeof(GCFreeChunk)) {
+ EarthShatteringKaBoom(3);
+ }
+ lastp = p;
+ p = (PRWord*) ((char*)p + cp->chunkSize);
+ }
+ }
+}
+
+static void CheckHeap(void) {
+ GCSeg *sp = segs;
+ GCSeg *esp = sp + nsegs;
+ while (sp < esp) {
+ CheckSegment(sp);
+ sp++;
+ }
+}
+
+#endif /* GC_CHECK */
+
+/******************************************************************************/
+
+#ifdef DEBUG
+long gc_thrash = -1L;
+#endif
+
+/*
+** Allocate memory from the GC Heap. Performs garbage collections if
+** memory gets tight and grows the heap as needed. May return NULL if
+** memory cannot be found.
+*/
+PR_IMPLEMENT(PRWord GCPTR *)PR_AllocMemory(
+ PRWord requestedBytes, PRInt32 tix, PRWord flags)
+{
+ PRWord *p;
+ CollectorType *ct;
+ PRInt32 bytes;
+ GCFinal *final = 0;
+ GCWeak *weak = 0;
+ int dub = flags & PR_ALLOC_DOUBLE;
+ PRInt32 objBytes;
+#ifdef GC_STATS
+ PRInt64 allocTime, ldelta;
+#endif
+
+ if (!allocationEnabled) return NULL;
+
+ PR_ASSERT(requestedBytes >= 0);
+ PR_ASSERT(_pr_collectorTypes[tix].flags != 0);
+
+#ifdef DEBUG
+ if (_pr_do_a_dump) {
+ /*
+ ** Collect, pause for a second (lets finalizer run), and then GC
+ ** again.
+ */
+ PR_GC();
+ PR_Sleep(PR_MicrosecondsToInterval(1000000L));
+ PR_GC();
+ PR_DumpGCHeap(_pr_dump_file, PR_TRUE);
+ _pr_do_a_dump = 0;
+ }
+#endif
+
+#ifdef GC_STATS
+ allocTime = PR_Now();
+#endif
+ bytes = (PRInt32) requestedBytes;
+
+ /*
+ ** Align bytes to a multiple of a PRWord, then add in enough space
+ ** to hold the header word.
+ **
+ ** MSVC 1.52 crashed on the ff. code because of the "complex" shifting :-(
+ */
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD) < bytes ) return NULL;
+ bytes = (bytes + PR_BYTES_PER_WORD - 1) >> PR_BYTES_PER_WORD_LOG2;
+ bytes <<= PR_BYTES_PER_WORD_LOG2;
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - sizeof(PRWord)) < bytes ) return NULL;
+ bytes += sizeof(PRWord);
+ /*
+ * Add in an extra word of memory for double-aligned memory. Some
+ * percentage of the time this will waste a word of memory (too
+ * bad). Howver, it makes the allocation logic much simpler and
+ * faster.
+ */
+#ifndef IS_64
+ if (dub) {
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD) < bytes ) return NULL;
+ bytes += PR_BYTES_PER_WORD;
+ }
+#endif
+
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Bloat the allocation a bit so that we can lay down
+ a check pattern that we will validate */
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD * 3) < bytes ) return NULL;
+ bytes += PR_BYTES_PER_WORD * 3;
+ }
+#endif
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ if ((MAX_INT - sizeof(GCBlockEnd)) < bytes ) return NULL;
+ bytes += sizeof(GCBlockEnd);
+#endif
+
+ PR_ASSERT( bytes < MAX_ALLOC_SIZE );
+ /*
+ ** Java can ask for objects bigger than MAX_ALLOC_SIZE,
+ ** but it won't get them.
+ */
+ if (bytes >= MAX_ALLOC_SIZE) return NULL;
+
+#ifdef DEBUG
+ if (gc_thrash == -1L ? (gc_thrash = (long)PR_GetEnv("GC_THRASH")):gc_thrash) PR_GC();
+#endif
+
+ ct = &_pr_collectorTypes[tix];
+ if (ct->flags & (_GC_TYPE_FINAL|_GC_TYPE_WEAK)) {
+ if (0 != ct->gctype.finalize) {
+ /*
+ ** Allocate a GCFinal struct for this object in advance. Don't put
+ ** it on the pending list until we have allocated the object
+ */
+ final = AllocFinalNode();
+ if (!final) {
+ /* XXX THIS IS NOT ACCEPTABLE*/
+ PR_ASSERT(0);
+ return 0;
+ }
+ }
+ if (0 != ct->gctype.getWeakLinkOffset) {
+ /*
+ ** Allocate a GCWeak struct for this object in advance. Don't put
+ ** it on the weak links list until we have allocated the object
+ */
+ weak = AllocWeakNode();
+ if (!weak) {
+ /* XXX THIS IS NOT ACCEPTABLE*/
+ if (0 != final) {
+ FreeFinalNode(final);
+ }
+ PR_ASSERT(0);
+ return 0;
+ }
+ }
+ }
+
+ LOCK_GC();
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) CheckHeap();
+ allocationCount++;
+#endif
+
+ /* Check for overflow of maximum size we can handle */
+ if (bytes > MAX_ALLOC) goto lost;
+
+ /* Try default allocation */
+ p = ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) ?
+ BigAlloc(tix, bytes, dub) : BinAlloc(tix, bytes, dub);
+ if (0 == p) {
+#ifdef GC_STATS
+ LL_SUB(ldelta, PR_Now(), allocTime);
+#endif
+ /* Collect some memory */
+ _GCTRACE(GC_ALLOC, ("force GC: want %d", bytes));
+ dogc();
+ PR_ASSERT( GC_IS_LOCKED() );
+
+ /* After a collection we check and see if we should grow the
+ ** heap. We grow the heap when the amount of memory free is less
+ ** than a certain percentage of the heap size. We don't check to
+ ** see if the grow succeeded because our fallback strategy in
+ ** either case is to try one more time to allocate. */
+ if ((_pr_gcData.allocMemory < _pr_gcData.maxMemory)
+ && ((_pr_gcData.freeMemory <
+ ((_pr_gcData.allocMemory * MIN_FREE_THRESHOLD_AFTER_GC) / 100L))
+ || (_pr_gcData.freeMemory < bytes))) {
+ GrowHeap(PR_MAX(bytes, segmentSize));
+ }
+#ifdef GC_STATS
+ LL_ADD(allocTime, PR_Now(), ldelta);
+#endif
+
+ /* Try again */
+ p = ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) ?
+ BigAlloc(tix, bytes, dub) : BinAlloc(tix, bytes, dub);
+ if (0 == p) {
+ /* Well that lost big time. Memory must be pretty well fragmented */
+ if (!GrowHeap(PR_MAX(bytes, segmentSize))) goto lost;
+ p = BinAlloc(tix, bytes, dub);
+ if (0 == p) goto lost;
+ }
+ }
+
+ /* Zero out the portion of the object memory that was used by
+ the GCFreeChunk structure (skip the first word because it
+ was already overwritten by the gc header word) */
+ objBytes = OBJ_BYTES(p[0]);
+ if (objBytes > sizeof(PRWord)) p[1] = 0;
+ if (objBytes > sizeof(PRWord)*2) p[2] = 0;
+
+ if (final) {
+ _GCTRACE(GC_ALLOC, ("alloc 0x%x (%d) final=0x%x",
+ p, bytes, final));
+ final->object = p;
+ PR_APPEND_LINK(&final->links, &_pr_finalizeableObjects);
+ } else {
+ _GCTRACE(GC_ALLOC, ("alloc 0x%x (%d)", p, bytes));
+ }
+ if (weak) {
+ weak->object = p;
+ PR_APPEND_LINK(&weak->links, &_pr_weakLinks);
+ }
+ METER(meter.allocBytes += bytes);
+ METER(meter.wastedBytes += (bytes - requestedBytes));
+ UNLOCK_GC();
+
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ {
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ end->check = PR_BLOCK_END;
+ }
+#endif
+#ifdef GC_STATS
+ {
+ PRInt64 now = PR_Now();
+ double delta;
+ PRInt32 bin;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+
+ end->allocTime = allocTime;
+ LL_SUB(ldelta, now, allocTime);
+ LL_L2D(delta, ldelta);
+ InlineBinNumber(bin, requestedBytes);
+ end->bin = bin;
+ gcstats[bin].nallocs++;
+ gcstats[bin].allocTime += delta;
+ gcstats[bin].allocTimeVariance += delta * delta;
+ }
+#endif
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Place a pattern in the memory that was allocated that was not
+ requested. We will check the pattern later. */
+ char* cp = (char*)(p + 1) + requestedBytes;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ char i = (char) 0xff;
+ while (cp < (char*)end) {
+ *cp++ = i--;
+ }
+ end->requestedBytes = requestedBytes;
+ CheckHeap();
+ }
+#endif
+ return p + 1;
+
+ lost:
+ /* Out of memory */
+ UNLOCK_GC();
+ if (final) {
+ FreeFinalNode(final);
+ }
+ if (weak) {
+ FreeWeakNode(weak);
+ }
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+ return 0;
+}
+
+/* Shortcut allocator for objects that do not require finalization or
+ are weak objects */
+PR_IMPLEMENT(PRWord GCPTR *)
+PR_AllocSimpleMemory(PRWord requestedBytes, PRInt32 tix)
+{
+ PRWord *p;
+ PRInt32 bytes;
+ PRInt32 objBytes;
+#ifdef GC_STATS
+ PRInt64 allocTime, ldelta;
+#endif
+
+ if (!allocationEnabled) return NULL;
+
+ PR_ASSERT(requestedBytes >= 0);
+ PR_ASSERT(_pr_collectorTypes[tix].flags != 0);
+
+#ifdef DEBUG
+ if (_pr_do_a_dump) {
+ /*
+ ** Collect, pause for a second (lets finalizer run), and then GC
+ ** again.
+ */
+ PR_GC();
+ PR_Sleep(PR_MicrosecondsToInterval(1000000L));
+ PR_GC();
+ PR_DumpGCHeap(_pr_dump_file, PR_TRUE);
+ _pr_do_a_dump = 0;
+ }
+#endif
+
+#ifdef GC_STATS
+ allocTime = PR_NowMS();
+#endif
+ bytes = (PRInt32) requestedBytes;
+
+ /*
+ ** Align bytes to a multiple of a PRWord, then add in enough space
+ ** to hold the header word.
+ **
+ ** MSVC 1.52 crashed on the ff. code because of the "complex" shifting :-(
+ */
+ bytes = (bytes + PR_BYTES_PER_WORD - 1) >> PR_BYTES_PER_WORD_LOG2;
+ bytes <<= PR_BYTES_PER_WORD_LOG2;
+ bytes += sizeof(PRWord);
+
+ /*
+ * Add in an extra word of memory for double-aligned memory. Some
+ * percentage of the time this will waste a word of memory (too
+ * bad). Howver, it makes the allocation logic much simpler and
+ * faster.
+ */
+#ifndef IS_64
+ bytes += PR_BYTES_PER_WORD;
+#endif
+
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Bloat the allocation a bit so that we can lay down
+ a check pattern that we will validate */
+ bytes += PR_BYTES_PER_WORD * 2;
+ }
+#endif
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ bytes += sizeof(GCBlockEnd);
+#endif
+
+ /* Java can ask for objects bigger than 4M, but it won't get them */
+ /*
+ * This check was added because there is a fundamental limit of
+ * the size field maintained by the gc code. Going over the 4M
+ * limit caused some bits to roll over into another bit field,
+ * violating the max segment size and causing a bug.
+ */
+ if (bytes >= MAX_ALLOC_SIZE) {
+ return NULL;
+ }
+#ifdef DEBUG
+ if (gc_thrash == -1L
+ ? (gc_thrash = (long)PR_GetEnv("GC_THRASH"))
+ : gc_thrash) {
+ PR_GC();
+ }
+#endif
+
+ LOCK_GC();
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ CheckHeap();
+ }
+ allocationCount++;
+#endif
+
+ /* Try default allocation */
+ if ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) {
+ p = BigAlloc(tix, bytes, 1);
+ } else {
+ p = BinAlloc(tix, bytes, 1);
+ }
+ if (0 == p) {
+#ifdef GC_STATS
+ LL_SUB(ldelta, PR_Now(), allocTime);
+#endif
+ /* Collect some memory */
+ _GCTRACE(GC_ALLOC, ("force GC: want %d", bytes));
+ dogc();
+ PR_ASSERT( GC_IS_LOCKED() );
+
+ /* After a collection we check and see if we should grow the
+ heap. We grow the heap when the amount of memory free is less
+ than a certain percentage of the heap size. We don't check to
+ see if the grow succeeded because our fallback strategy in
+ either case is to try one more time to allocate. */
+ if ((_pr_gcData.allocMemory < _pr_gcData.maxMemory) &&
+ (_pr_gcData.freeMemory <
+ ((_pr_gcData.allocMemory * MIN_FREE_THRESHOLD_AFTER_GC) / 100L))) {
+ GrowHeap(PR_MAX(bytes, segmentSize));
+ }
+#ifdef GC_STATS
+ LL_ADD(allocTime, PR_Now(), ldelta);
+#endif
+
+ /* Try one last time */
+ if ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) {
+ p = BigAlloc(tix, bytes, 1);
+ } else {
+ p = BinAlloc(tix, bytes, 1);
+ }
+ if (0 == p) {
+ /* Well that lost big time. Memory must be pretty well fragmented */
+ if (!GrowHeap(PR_MAX(bytes, segmentSize))) {
+ goto lost;
+ }
+ p = BinAlloc(tix, bytes, 1);
+ if (0 == p) goto lost;
+ }
+ }
+
+ /* Zero out the portion of the object memory that was used by
+ the GCFreeChunk structure (skip the first word because it
+ was already overwritten by the gc header word) */
+ objBytes = OBJ_BYTES(p[0]);
+ if (objBytes > sizeof(PRWord)) p[1] = 0;
+ if (objBytes > sizeof(PRWord)*2) p[2] = 0;
+
+ METER(meter.allocBytes += bytes);
+ METER(meter.wastedBytes += (bytes - requestedBytes));
+ UNLOCK_GC();
+
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ {
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ end->check = PR_BLOCK_END;
+ }
+#endif
+#ifdef GC_STATS
+ {
+ PRInt64 now = PR_Now();
+ double delta;
+ PRInt32 bin;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+
+ end->allocTime = allocTime;
+ LL_SUB(ldelta, now, allocTime);
+ LL_L2D(delta, ldelta);
+ InlineBinNumber(bin, requestedBytes);
+ end->bin = bin;
+ gcstats[bin].nallocs++;
+ gcstats[bin].allocTime += delta;
+ gcstats[bin].allocTimeVariance += delta * delta;
+ }
+#endif
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Place a pattern in the memory that was allocated that was not
+ requested. We will check the pattern later. */
+ char* cp = (char*)(p + 1) + requestedBytes;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ char i = (char) 0xff;
+ while (cp < (char*)end) {
+ *cp++ = i--;
+ }
+ end->requestedBytes = requestedBytes;
+ CheckHeap();
+ }
+#endif
+ return p + 1;
+
+ lost:
+ /* Out of memory */
+ UNLOCK_GC();
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+ return 0;
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(PRWord) PR_GetObjectHeader(void *ptr) {
+ GCSeg *sp;
+ PRWord *h;
+
+ if (ptr == 0) return 0;
+ sp = InHeap(ptr);
+ if (sp == 0) return 0;
+ h = (PRWord*)FindObject(sp, (PRWord*)ptr);
+ return GC_GET_USER_BITS(h[0]);
+}
+
+PR_IMPLEMENT(PRWord) PR_SetObjectHeader(void *ptr, PRWord newUserBits) {
+ GCSeg *sp;
+ PRWord *h, rv;
+
+ if (ptr == 0) return 0;
+ sp = InHeap(ptr);
+ if (sp == 0) return 0;
+ h = (PRWord*)FindObject(sp, (PRWord*)ptr);
+ rv = GC_GET_USER_BITS(h[0]);
+ h[0] = (h[0] & ~GC_USER_BITS) |
+ ((newUserBits << GC_USER_BITS_SHIFT) & GC_USER_BITS);
+ return rv;
+}
+
+PR_IMPLEMENT(void) PR_InitGC(
+ PRWord flags, PRInt32 initialHeapSize, PRInt32 segSize, PRThreadScope scope)
+{
+ static char firstTime = 1;
+
+ if (!firstTime) return;
+ firstTime = 0;
+
+ _pr_msgc_lm = PR_NewLogModule("msgc");
+ _pr_pageShift = PR_GetPageShift();
+ _pr_pageSize = PR_GetPageSize();
+
+ /* Setup initial heap size and initial segment size */
+ if (0 != segSize) segmentSize = segSize;
+#ifdef DEBUG
+ GC = PR_NewLogModule("GC");
+ {
+ char *ev = PR_GetEnv("GC_SEGMENT_SIZE");
+ if (ev && ev[0]) {
+ PRInt32 newSegmentSize = atoi(ev);
+ if (0 != newSegmentSize) segmentSize = newSegmentSize;
+ }
+ ev = PR_GetEnv("GC_INITIAL_HEAP_SIZE");
+ if (ev && ev[0]) {
+ PRInt32 newInitialHeapSize = atoi(ev);
+ if (0 != newInitialHeapSize) initialHeapSize = newInitialHeapSize;
+ }
+ ev = PR_GetEnv("GC_FLAGS");
+ if (ev && ev[0]) {
+ flags |= atoi(ev);
+ }
+#ifdef GCMETER
+ ev = PR_GetEnv("GC_METER");
+ if (ev && ev[0]) {
+ _pr_gcMeter = atoi(ev);
+ }
+#endif
+ }
+#endif
+ if (0 == initialHeapSize) initialHeapSize = segmentSize;
+ if (initialHeapSize < segmentSize) initialHeapSize = segmentSize;
+
+ _pr_gcData.maxMemory = MAX_SEGS * segmentSize;
+ _pr_gcData.liveBlock = ProcessRootBlock;
+ _pr_gcData.livePointer = ProcessRootPointer;
+ _pr_gcData.processRootBlock = ProcessRootBlock;
+ _pr_gcData.processRootPointer = ProcessRootPointer;
+ _pr_gcData.dumpOutput = NULL;
+
+ PR_INIT_CLIST(&_pr_finalizeableObjects);
+ PR_INIT_CLIST(&_pr_finalQueue);
+ _PR_InitGC(flags);
+
+ /* Create finalizer thread */
+ _PR_CreateFinalizer(scope);
+
+ /* Allocate the initial segment for the heap */
+ minBin = 31;
+ maxBin = 0;
+ GrowHeap(initialHeapSize);
+ PR_RegisterRootFinder(ScanWeakFreeList, "scan weak free list", 0);
+}
+
+/** Added by Vishy for sanity checking a few GC structures **/
+/** Can use SanityCheckGC to debug corrupted GC Heap situations **/
+
+#ifdef DEBUG
+
+static int SegmentOverlaps(int i, int j)
+{
+ return
+ (((segs[i].limit > segs[j].base) && (segs[i].base < segs[j].base)) ||
+ ((segs[j].limit > segs[i].base) && (segs[j].base < segs[i].base)));
+}
+
+static void NoSegmentOverlaps(void)
+{
+ int i,j;
+
+ for (i = 0; i < nsegs; i++)
+ for (j = i+1 ; j < nsegs ; j++)
+ PR_ASSERT(!SegmentOverlaps(i,j));
+}
+
+static void SegInfoCheck(void)
+{
+ int i;
+ for (i = 0 ; i < nsegs ; i++)
+ PR_ASSERT((segs[i].info->hbits) &&
+ (segs[i].info->hbits == segs[i].hbits) &&
+ (segs[i].info->base == segs[i].base) &&
+ (segs[i].info->limit == segs[i].limit));
+}
+
+static void SanityCheckGC()
+{
+ NoSegmentOverlaps();
+ SegInfoCheck();
+}
+
+#endif
+
+#if defined(DEBUG) && defined(WIN32)
+
+extern void *baseaddr;
+extern void *lastaddr;
+
+PR_IMPLEMENT(void)
+PR_PrintGCStats(void)
+{
+ long reportedSegSpace = _pr_gcData.busyMemory + _pr_gcData.freeMemory;
+ char* msg;
+ long largeCount = 0, largeSize = 0;
+ long segCount = 0, segSize = 0;
+ long freeCount = 0, freeSize = 0;
+ GCSeg *sp, *esp;
+ GCSegInfo* si;
+
+ LOCK_GC();
+
+ sp = segs;
+ esp = sp + nsegs;
+ while (sp < esp) {
+ long size = sp->info->limit - sp->info->base;
+ segCount++;
+ segSize += size;
+ if (sp->info->fromMalloc) {
+ largeCount++;
+ largeSize += size;
+ }
+ sp++;
+ }
+
+ si = freeSegs;
+ while (si != NULL) {
+ long size = si->limit - si->base;
+ freeCount++;
+ freeSize += size;
+ si = si->next;
+ }
+
+ msg = PR_smprintf("\
+# GC Stats:\n\
+# vm space:\n\
+# range: %ld - %ld\n\
+# size: %ld\n\
+# segments:\n\
+# range: %ld - %ld\n\
+# count: %ld (reported: %ld)\n\
+# size: %ld (reported: %ld)\n\
+# free count: %ld\n\
+# free size: %ld\n\
+# busy objs: %ld (%ld%%)\n\
+# free objs: %ld (%ld%%)\n\
+# large blocks:\n\
+# count: %ld\n\
+# total size: %ld (%ld%%)\n\
+# avg size: %ld\n\
+",
+ /* vm space */
+ (long)baseaddr, (long)lastaddr,
+ (long)lastaddr - (long)baseaddr,
+ /* segments */
+ _pr_gcData.lowSeg, _pr_gcData.highSeg,
+ segCount, nsegs,
+ segSize, reportedSegSpace,
+ freeCount,
+ freeSize,
+ _pr_gcData.busyMemory,
+ (_pr_gcData.busyMemory * 100 / reportedSegSpace),
+ _pr_gcData.freeMemory,
+ (_pr_gcData.freeMemory * 100 / reportedSegSpace),
+ /* large blocks */
+ largeCount,
+ largeSize, (largeSize * 100 / reportedSegSpace),
+ (largeCount ? largeSize / largeCount : 0)
+ );
+ UNLOCK_GC();
+ fprintf(stderr, msg);
+ OutputDebugString(msg);
+ PR_smprintf_free(msg);
+#ifdef GC_STATS
+ PR_PrintGCAllocStats();
+#endif
+}
+#endif
+
+PR_IMPLEMENT(void)
+PR_DumpToFile(char* filename, char* msg, PRFileDumper dump, PRBool detailed)
+{
+ FILE *out;
+ OutputDebugString(msg);
+ out = fopen(filename, "a");
+ if (!out) {
+ char buf[64];
+ PR_ASSERT(strlen(filename) < sizeof(buf) - 16);
+ PR_snprintf(buf, sizeof(buf), "Can't open \"%s\"\n",
+ filename);
+ OutputDebugString(buf);
+ }
+ else
+ {
+ struct tm *newtime;
+ time_t aclock;
+ int i;
+
+ time(&aclock);
+ newtime = localtime(&aclock);
+ fprintf(out, "%s on %s\n", msg, asctime(newtime)); /* Print current time */
+ dump(out, detailed);
+ fprintf(out, "\n\n");
+ for (i = 0; i < 80; i++)
+ fprintf(out, "=");
+ fprintf(out, "\n\n");
+ fclose(out);
+ }
+ OutputDebugString(" done\n");
+}
+
diff --git a/lib/msgc/src/unixgc.c b/lib/msgc/src/unixgc.c
new file mode 100644
index 0000000..cea4225
--- /dev/null
+++ b/lib/msgc/src/unixgc.c
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "gcint.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#define _PR_GC_VMBASE 0x40000000
+
+#if defined(SOLARIS)
+#define _MD_MMAP_FLAGS MAP_SHARED
+#elif defined(RELIANTUNIX)
+#define _MD_MMAP_FLAGS MAP_PRIVATE|MAP_FIXED
+#else
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+#endif
+
+static PRInt32 zero_fd = -1;
+static PRLock *zero_fd_lock = NULL;
+
+void _MD_InitGC(void)
+{
+#ifdef DEBUG
+ /*
+ * Disable using mmap(2) if NSPR_NO_MMAP is set
+ */
+ if (getenv("NSPR_NO_MMAP")) {
+ zero_fd = -2;
+ return;
+ }
+#endif
+ zero_fd = open("/dev/zero",O_RDWR , 0);
+ zero_fd_lock = PR_NewLock();
+}
+
+/* This static variable is used by _MD_GrowGCHeap and _MD_ExtendGCHeap */
+static void *lastaddr = (void*) _PR_GC_VMBASE;
+
+void *_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+ PRUint32 size;
+
+ size = *sizep;
+
+ PR_Lock(zero_fd_lock);
+ if (zero_fd < 0) {
+ goto mmap_loses;
+ }
+
+ /* Extend the mapping */
+ addr = mmap(lastaddr, size, PROT_READ|PROT_WRITE|PROT_EXEC,
+ _MD_MMAP_FLAGS,
+ zero_fd, 0);
+ if (addr == (void*)-1) {
+ zero_fd = -1;
+ goto mmap_loses;
+ }
+ lastaddr = ((char*)addr + size);
+#ifdef DEBUG
+ PR_LOG(_pr_msgc_lm, PR_LOG_WARNING,
+ ("GC: heap extends from %08x to %08x\n",
+ _PR_GC_VMBASE,
+ _PR_GC_VMBASE + (char*)lastaddr - (char*)_PR_GC_VMBASE));
+#endif
+ PR_Unlock(zero_fd_lock);
+ return addr;
+
+mmap_loses:
+ PR_Unlock(zero_fd_lock);
+ return PR_MALLOC(size);
+}
+
+/* XXX - This is disabled. MAP_FIXED just does not work. */
+#if 0
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ PRBool rv = PR_FALSE;
+ void* addr;
+ PRInt32 allocSize = newSize - oldSize;
+
+ PR_Lock(zero_fd_lock);
+ addr = mmap(base + oldSize, allocSize, PROT_READ|PROT_WRITE|PROT_EXEC,
+ _MD_MMAP_FLAGS | MAP_FIXED, zero_fd, 0);
+ if (addr == (void*)-1) {
+ goto loser;
+ }
+ if (addr != (void*) (base + oldSize)) {
+ munmap(base + oldSize, allocSize);
+ goto loser;
+ }
+ lastaddr = ((char*)base + newSize);
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS,
+ ("GC: heap now extends from %p to %p",
+ base, base + newSize));
+ rv = PR_TRUE;
+
+loser:
+ PR_Unlock(zero_fd_lock);
+ return rv;
+}
+#else
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ return PR_FALSE;
+}
+#endif
+
+void _MD_FreeGCSegment(void *base, PRInt32 len)
+{
+ if (zero_fd < 0) {
+ PR_DELETE(base);
+ } else {
+ (void) munmap(base, len);
+ }
+}
diff --git a/lib/msgc/src/win32gc.c b/lib/msgc/src/win32gc.c
new file mode 100644
index 0000000..eec8377
--- /dev/null
+++ b/lib/msgc/src/win32gc.c
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * GC related routines
+ *
+ */
+#include <windows.h>
+#include "prlog.h"
+
+extern PRLogModuleInfo* _pr_msgc_lm;
+
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+
+void *baseaddr = (void*) GC_VMBASE;
+void *lastaddr = (void*) GC_VMBASE;
+
+void _MD_InitGC() {}
+
+void *_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+ size_t size;
+
+ /* Reserve a block of memory for the GC */
+ if( lastaddr == baseaddr ) {
+ addr = VirtualAlloc( (void *)GC_VMBASE, GC_VMLIMIT, MEM_RESERVE, PAGE_READWRITE );
+
+ /*
+ ** If the GC_VMBASE address is already mapped, then let the OS choose a
+ ** base address that is available...
+ */
+ if (addr == NULL) {
+ addr = VirtualAlloc( NULL, GC_VMLIMIT, MEM_RESERVE, PAGE_READWRITE );
+
+ baseaddr = lastaddr = addr;
+ if (addr == NULL) {
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("GC: unable to allocate heap: LastError=%ld",
+ GetLastError()));
+ return 0;
+ }
+ }
+ }
+ size = *sizep;
+
+ /* Extend the mapping */
+ addr = VirtualAlloc( lastaddr, size, MEM_COMMIT, PAGE_READWRITE );
+ if (addr == NULL) {
+ return 0;
+ }
+
+ lastaddr = ((char*)addr + size);
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS,
+ ("GC: heap extends from %08x to %08x",
+ baseaddr, (long)baseaddr + (char*)lastaddr - (char*)baseaddr));
+
+ return addr;
+}
+
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ void* addr;
+
+ addr = VirtualAlloc( base + oldSize, newSize - oldSize,
+ MEM_COMMIT, PAGE_READWRITE );
+ if (NULL == addr) {
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("GC: unable to extend heap: LastError=%ld",
+ GetLastError()));
+ return PR_FALSE;
+ }
+ if (base + oldSize != (char*)addr) {
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("GC: segment extension returned %x instead of %x",
+ addr, base + oldSize));
+ VirtualFree(addr, newSize - oldSize, MEM_DECOMMIT);
+ return PR_FALSE;
+ }
+ lastaddr = base + newSize;
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS,
+ ("GC: heap now extends from %p to %p",
+ base, base + newSize));
+ return PR_TRUE;
+}
+
+
+void _MD_FreeGCSegment(void *base, PRInt32 len)
+{
+ (void)VirtualFree(base, 0, MEM_RELEASE);
+}
diff --git a/lib/msgc/tests/.cvsignore b/lib/msgc/tests/.cvsignore
new file mode 100644
index 0000000..f3c7a7c
--- /dev/null
+++ b/lib/msgc/tests/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/lib/msgc/tests/Makefile.in b/lib/msgc/tests/Makefile.in
new file mode 100644
index 0000000..51031ed
--- /dev/null
+++ b/lib/msgc/tests/Makefile.in
@@ -0,0 +1,270 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape Portable Runtime (NSPR).
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifeq ($(OS_TARGET), OS2)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+CSRCS = gc1.c thrashgc.c
+
+ifeq (,$(filter-out WINNT OS2,$(OS_ARCH)))
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS) $(OBJS)
+
+INCLUDES = -I$(dist_includedir)
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(dist_libdir)
+NSPR_VERSION = $(MOD_MAJOR_VERSION)
+GC_VERSION = $(MOD_MAJOR_VERSION)
+LIBPR = -lnspr$(NSPR_VERSION)
+LIBPLC = -lplc$(NSPR_VERSION)
+LIBGC = -lmsgc$(GC_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ LDOPTS = -NOLOGO -DEBUG -INCREMENTAL:NO
+ LIBPR = $(dist_libdir)/libnspr$(NSPR_VERSION).$(LIB_SUFFIX)
+ LIBPLC = $(dist_libdir)/libplc$(NSPR_VERSION).$(LIB_SUFFIX)
+ LIBGC= $(dist_libdir)/libmsgc$(GC_VERSION).$(LIB_SUFFIX)
+endif
+
+ifeq ($(OS_ARCH),OS2)
+ LDOPTS += -Zomf -Zlinker /PM:VIO
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+LDOPTS += -rpath $(PWD)/$(dist_libdir) -rdata_shared
+
+# For 6.x machines, include this flag
+ifeq ($(basename $(OS_RELEASE)),6)
+ifeq ($(USE_N32),1)
+LDOPTS += -n32
+else
+LDOPTS += -32
+endif
+endif
+
+endif
+
+ifeq ($(OS_ARCH), OSF1)
+# I haven't figured out how to pass -rpath to cc on OSF1 V3.2, so
+# we do static linking.
+ifeq ($(OS_RELEASE), V3.2)
+ LIBPR = $(dist_libdir)/libnspr$(NSPR_VERSION).a
+ LIBPLC = $(dist_libdir)/libplc$(NSPR_VERSION).a
+ LIBGC = $(dist_libdir)/libmsgc$(GC_VERSION).a
+ EXTRA_LIBS = -lc_r
+else
+ LDOPTS += -rpath $(PWD)/$(dist_libdir)
+endif
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+LDOPTS += -z -Wl,+s,+b,$(PWD)/$(dist_libdir)
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+LDOPTS += -blibpath:$(PWD)/$(dist_libdir):/usr/lib:/lib
+ifeq ($(OS_ARCH)$(OS_RELEASE),AIX4.1)
+LIBPR = -lnspr$(NSPR_VERSION)_shr
+LIBPLC = -lplc$(NSPR_VERSION)_shr
+LIBGC = -lmsgc$(GC_VERSION)_shr
+else
+LDOPTS += -brtl
+EXTRA_LIBS = -ldl
+endif
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ifneq ($(OS_RELEASE), 4.1.3_U1)
+ifdef NS_USE_GCC
+LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(dist_libdir)
+else
+LDOPTS += -R $(PWD)/$(dist_libdir)
+endif
+endif
+
+# SunOS 5.5 needs to link with -lpthread, even though we already
+# linked with this system library when we built libnspr.so.
+ifeq ($(OS_RELEASE), 5.5)
+ifdef USE_PTHREADS
+EXTRA_LIBS = -lpthread
+endif
+endif
+endif # SunOS
+
+ifeq ($(OS_ARCH),NEC)
+EXTRA_LIBS = $(OS_LIBS)
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+ifeq ($(OS_ARCH), NCR)
+# XXX: We see some strange problems when we link with libnspr.so.
+# So for now we use static libraries on NCR. The shared library
+# stuff below is commented out.
+LIBPR = $(dist_libdir)/libnspr$(NSPR_VERSION).a
+LIBPLC = $(dist_libdir)/libplc$(NSPR_VERSION).a
+LIBGC = $(dist_libdir)/libmsgc$(GC_VERSION).a
+EXTRA_LIBS = -lsocket -lnsl -ldl
+
+# NCR needs to link against -lsocket -lnsl (and -lc, which is linked
+# implicitly by $(CC)) again even though we already linked with these
+# system libraries when we built libnspr.so.
+#EXTRA_LIBS = -lsocket -lnsl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+#export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+ifeq ($(OS_ARCH), Linux)
+ifeq ($(OS_RELEASE), 1.2)
+EXTRA_LIBS = -ldl
+endif
+endif
+
+ifeq ($(OS_ARCH), SCOOS)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+ifeq ($(OS_ARCH),SINIX)
+EXTRA_LIBS = -lsocket -lnsl -lresolv -ldl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+ifeq ($(OS_ARCH),BSD_OS)
+EXTRA_LIBS = -ldl
+endif
+
+ifeq ($(OS_ARCH),DGUX)
+EXTRA_LIBS = -lsocket -lnsl -ldl
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(topsrcdir)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(dist_libdir)/libnspr$(NSPR_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(OS_ARCH), WINNT)
+ link $(LDOPTS) $< $(LIBGC) $(LIBPLC) $(LIBPR) wsock32.lib -out:$@
+else
+ifeq ($(OS_ARCH),OS2)
+ $(LINK) $(LDOPTS) $< $(LIBGC) $(LIBPLC) $(LIBPR) $(OS_LIBS) $(EXTRA_LIBS) -o $@
+else
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBGC) $(LIBPLC) $(LIBPR) $(EXTRA_LIBS) -o $@
+endif
+endif
+endif
+
+export:: $(TARGETS)
+clean::
+ rm -f $(TARGETS)
diff --git a/lib/msgc/tests/gc1.c b/lib/msgc/tests/gc1.c
new file mode 100644
index 0000000..9bd485e
--- /dev/null
+++ b/lib/msgc/tests/gc1.c
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prgc.h"
+#include "prinit.h"
+#include "prmon.h"
+#include "prinrval.h"
+#include "private/pprthred.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static PRMonitor *mon;
+static PRInt32 threads, waiting, iterations;
+static PRInt32 scanCount, finalizeCount, freeCount;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+typedef struct Array {
+ PRUintn size;
+ void *body[1];
+} Array;
+
+int arrayTypeIndex;
+
+static void PR_CALLBACK ScanArray(void *a)
+{
+/* printf ("In ScanArray a = %X size = %d \n", a, a->size); */
+ scanCount++;
+}
+
+static void PR_CALLBACK FinalizeArray(void *a)
+{
+/* printf ("In FinalizeArray a = %X size = %d \n", a, a->size); */
+ finalizeCount++;
+}
+
+static void PR_CALLBACK FreeArray(void *a)
+{
+/* printf ("In FreeArray\n"); */
+ freeCount++;
+}
+
+static Array *NewArray(PRUintn size)
+{
+ Array *a;
+
+ a = (Array *)PR_AllocMemory(sizeof(Array) + size*sizeof(void*) - 1*sizeof(void*),
+ arrayTypeIndex, PR_ALLOC_CLEAN);
+
+/* printf ("In NewArray a = %X \n", a); */
+
+ if (a)
+ a->size = size;
+ return a;
+}
+
+GCType arrayType = {
+ ScanArray,
+ FinalizeArray,
+ 0,
+ 0,
+ FreeArray,
+ 0
+};
+
+static void Initialize(void)
+{
+ PR_InitGC(0, 0, 0, PR_GLOBAL_THREAD);
+ arrayTypeIndex = PR_RegisterType(&arrayType);
+}
+
+static void PR_CALLBACK AllocateLikeMad(void *arg)
+{
+ Array *prev;
+ PRInt32 i;
+ PRInt32 count;
+
+ count = (PRInt32)arg;
+ prev = 0;
+ for (i = 0; i < count; i++) {
+ Array *leak = NewArray(i & 511);
+ if ((i & 1023) == 0) {
+ prev = 0; /* forget */
+ } else {
+ if (i & 1) {
+ prev = leak; /* remember */
+ }
+ }
+ }
+ PR_EnterMonitor(mon);
+ waiting++;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+int main(int argc, char **argv)
+{
+ PRIntervalTime start, stop, usec;
+ double d;
+ PRIntn i, totalIterations;
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:");
+
+ threads = 10;
+ iterations = 100;
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ fprintf(stderr, "Invalid command-line option\n");
+ exit(1);
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 't': /* number of threads */
+ threads = atoi(opt->value);
+ break;
+ case 'c': /* iteration count */
+ iterations = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ fprintf(stderr, "t is %ld, i is %ld\n", (long) threads, (long) iterations);
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 5);
+ PR_STDIO_INIT();
+ Initialize();
+
+ /* Spin all of the allocator threads and then wait for them to exit */
+ start = PR_IntervalNow();
+ mon = PR_NewMonitor();
+ PR_EnterMonitor(mon);
+ waiting = 0;
+ for (i = 0; i < threads; i++) {
+ (void) PR_CreateThreadGCAble(PR_USER_THREAD,
+ AllocateLikeMad, (void*)iterations,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ }
+ while (waiting != threads) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+ PR_GC();
+ PR_ForceFinalize();
+
+ totalIterations = iterations * threads;
+/*
+ if (scanCount != totalIterations)
+ printf ("scanCount discrepancy scanCount = %d totalIterations = %d \n",
+ scanCount, totalIterations);
+ if (freeCount != totalIterations)
+ printf ("freeCount discrepancy freeCount = %d totalIterations = %d \n",
+ freeCount, totalIterations);
+ if ((finalizeCount != totalIterations) && (finalizeCount != (totalIterations-1)))
+ printf ("finalizeCount discrepancy finalizeCount = %d totalIterations = %d \n",
+ finalizeCount,totalIterations);
+*/
+
+ stop = PR_IntervalNow();
+
+ usec = stop = stop - start;
+ d = (double)usec;
+
+ if (debug_mode) printf("%40s: %6.2f usec\n", "GC allocation", d / (iterations * threads));
+ else {
+ if (d == 0.0) failed_already = PR_TRUE;
+
+ }
+
+ PR_Cleanup();
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+}
diff --git a/lib/msgc/tests/thrashgc.c b/lib/msgc/tests/thrashgc.c
new file mode 100644
index 0000000..a440d49
--- /dev/null
+++ b/lib/msgc/tests/thrashgc.c
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/***********************************************************************
+** Name: thrashgc
+**
+** Description: test garbace collection functions.
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include "prthread.h"
+#include "prgc.h"
+#include "prprf.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prinit.h"
+#include "prcvar.h"
+
+#include "private/pprthred.h"
+
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static char* progname;
+static PRInt32 loops = 1000;
+static int tix1, tix2, tix3;
+static GCInfo* gcInfo;
+static PRLock* stderrLock;
+
+typedef struct Type1 Type1;
+typedef struct Type2 Type2;
+
+struct Type1 {
+ Type2* atwo;
+ Type1* next;
+};
+
+struct Type2 {
+ void* buf;
+};
+
+static void PR_CALLBACK ScanType1(void *obj) {
+ gcInfo->livePointer(((Type1 *)obj)->atwo);
+ gcInfo->livePointer(((Type1 *)obj)->next);
+}
+
+static void PR_CALLBACK ScanType2(void *obj) {
+ gcInfo->livePointer(((Type2 *)obj)->buf);
+}
+
+static GCType type1 = {
+ ScanType1
+};
+
+static GCType type2 = {
+ ScanType2
+/* (void (*)(void*)) ScanType2 */
+};
+
+static GCType type3 = {
+ 0
+};
+
+Type1* NewType1(void) {
+ Type1* p = (Type1*) PR_AllocMemory(sizeof(Type1), tix1, PR_ALLOC_DOUBLE);
+ PR_ASSERT(p != NULL);
+ return p;
+}
+
+Type2* NewType2(void) {
+ Type2* p = (Type2*) PR_AllocMemory(sizeof(Type2), tix2, PR_ALLOC_DOUBLE);
+ PR_ASSERT(p != NULL);
+ return p;
+}
+
+void* NewBuffer(PRInt32 size) {
+ void* p = PR_AllocMemory(size, tix3, PR_ALLOC_DOUBLE);
+ PR_ASSERT(p != NULL);
+ return p;
+}
+
+/* Allocate alot of garbage */
+static void PR_CALLBACK AllocStuff(void *unused) {
+ PRInt32 i;
+ void* danglingRefs[50];
+ PRIntervalTime start, end;
+ char msg[100];
+
+ start = PR_IntervalNow();
+ for (i = 0; i < loops; i++) {
+ void* p;
+ if (i & 1) {
+ Type1* t1 = NewType1();
+ t1->atwo = NewType2();
+ t1->next = NewType1();
+ t1->atwo->buf = NewBuffer(100);
+ p = t1;
+ } else {
+ Type2* t2 = NewType2();
+ t2->buf = NewBuffer(i & 16383);
+ p = t2;
+ }
+ if ((i % 10) == 0) {
+ memmove(&danglingRefs[0], &danglingRefs[1], 49*sizeof(void*));
+ danglingRefs[49] = p;
+ }
+ }
+ end = PR_IntervalNow();
+ if (debug_mode) PR_snprintf(msg, sizeof(msg), "Thread %p: %ld allocations took %ld ms",
+ PR_GetCurrentThread(), loops,
+ PR_IntervalToMilliseconds((PRIntervalTime) (end - start)));
+ PR_Lock(stderrLock);
+ fprintf(stderr, "%s\n", msg);
+ PR_Unlock(stderrLock);
+ }
+
+static void usage(char *progname) {
+ fprintf(stderr, "Usage: %s [-t threads] [-l loops]\n", progname);
+ exit(-1);
+}
+
+static int realMain(int argc, char **argv, char *notused) {
+ int i;
+ int threads = 0;
+
+ progname = strrchr(argv[0], '/');
+ if (progname == 0) progname = argv[0];
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-t") == 0) {
+ if (i == argc - 1) {
+ usage(progname);
+ }
+ threads = atoi(argv[++i]);
+ if (threads < 0) threads = 0;
+ if (threads > 10000) threads = 10000;
+ continue;
+ }
+ if (strcmp(argv[i], "-l") == 0) {
+ if (i == argc - 1) {
+ usage(progname);
+ }
+ loops = atoi(argv[++i]);
+ continue;
+ }
+ usage(progname);
+ }
+
+ for (i = 0; i < threads; i++) {
+ PRThread* thread;
+
+ /* XXXXX */
+ thread = PR_CreateThreadGCAble(PR_USER_THREAD, /* thread type */
+ AllocStuff, /* start function */
+ NULL, /* arg */
+ PR_PRIORITY_NORMAL, /* priority */
+ PR_LOCAL_THREAD, /* thread scope */
+ PR_UNJOINABLE_THREAD, /* thread state */
+ 0); /* stack size */
+ if (thread == 0) {
+ fprintf(stderr, "%s: no more threads (only %d were created)\n",
+ progname, i);
+ break;
+ }
+ }
+ AllocStuff(NULL);
+ return 0;
+}
+
+static int padMain(int argc, char **argv) {
+ char pad[512];
+ return realMain(argc, argv, pad);
+}
+
+int main(int argc, char **argv) {
+ int rv;
+
+ debug_mode = 1;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_SetThreadGCAble();
+
+ PR_InitGC(0, 0, 0, PR_GLOBAL_THREAD);
+ PR_STDIO_INIT();
+ stderrLock = PR_NewLock();
+ tix1 = PR_RegisterType(&type1);
+ tix2 = PR_RegisterType(&type2);
+ tix3 = PR_RegisterType(&type3);
+ gcInfo = PR_GetGCInfo();
+ rv = padMain(argc, argv);
+ printf("PASS\n");
+ PR_Cleanup();
+ return rv;
+}