diff options
Diffstat (limited to 'src/libs/xpcom18a4/nsprpub/lib/msgc')
20 files changed, 6003 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/.cvsignore b/src/libs/xpcom18a4/nsprpub/lib/msgc/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/Makefile.in b/src/libs/xpcom18a4/nsprpub/lib/msgc/Makefile.in new file mode 100644 index 00000000..d7299b2b --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/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/src/libs/xpcom18a4/nsprpub/lib/msgc/include/.cvsignore b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/include/MANIFEST b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/MANIFEST new file mode 100644 index 00000000..a45ec20e --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/MANIFEST @@ -0,0 +1,5 @@ +# +# This is a list of local files which get copied to the mozilla:dist directory +# + +prgc.h diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/include/Makefile.in b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/Makefile.in new file mode 100644 index 00000000..60803a02 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/Makefile.in @@ -0,0 +1,61 @@ +# +# ***** 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) +ifeq ($(MOZ_BITS),16) + $(INSTALL) -m 444 $(EXPORT_HEADERS) $(MOZ_INCL) +endif + diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/include/gcint.h b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/gcint.h new file mode 100644 index 00000000..10048f06 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/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/src/libs/xpcom18a4/nsprpub/lib/msgc/include/prgc.h b/src/libs/xpcom18a4/nsprpub/lib/msgc/include/prgc.h new file mode 100644 index 00000000..add30213 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/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 syncronization 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; /* higest 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/src/libs/xpcom18a4/nsprpub/lib/msgc/src/.cvsignore b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/src/Makefile.in b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/Makefile.in new file mode 100644 index 00000000..e822fc29 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/Makefile.in @@ -0,0 +1,103 @@ +# +# ***** 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) +ifdef NS_USE_GCC +DLLBASE=-Wl,--image-base -Wl,0x30000000 +else +DLLBASE=/BASE:0x30000000 +endif # GCC +#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 +ifeq ($(MOZ_BITS),16) + $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/lib + $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/bin +endif + diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/src/macgc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/macgc.c new file mode 100644 index 00000000..eafc28e8 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/macgc.c @@ -0,0 +1,75 @@ +/* -*- 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 "primpl.h" +#include "MacMemAllocator.h" + +void _MD_InitGC() {} + +void *_MD_GrowGCHeap(size_t *sizep) +{ + void *heapPtr = NULL; + size_t heapSize = *sizep; + + // In previous versions of this code we tried to allocate GC heaps from the application + // heap. In the 4.0 application, we try to keep our app heap allications to a minimum + // and instead go through our own memory allocation routines. + heapPtr = malloc(heapSize); + + if (heapPtr == NULL) { + FreeMemoryStats stats; + + memtotal(heapSize, &stats); // How much can we allcoate? + + if (stats.maxBlockSize < heapSize) + heapSize = stats.maxBlockSize; + + heapPtr = malloc(heapSize); + + if (heapPtr == NULL) // Now we're hurting + heapSize = 0; + } + + *sizep = heapSize; + return heapPtr; +} + + +void _MD_FreeGCSegment(void *base, int32 /* len */) +{ + free(base); +} diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/src/os2gc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/os2gc.c new file mode 100644 index 00000000..f99bb678 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/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/src/libs/xpcom18a4/nsprpub/lib/msgc/src/prgcapi.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/prgcapi.c new file mode 100644 index 00000000..23f7ea51 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/prgcapi.c @@ -0,0 +1,351 @@ +/* -*- 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" +#if defined(XP_MAC) +#include "pprthred.h" +#else +#include "private/pprthred.h" +#endif +#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) +{ +#if defined(XP_MAC) +#pragma unused (t, closure) +#endif + + _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" + +#if defined(WIN16) +static FILE *tracefile = 0; +#endif + +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); +#if defined(WIN16) + if ( tracefile == 0 ) + { + tracefile = fopen( "xxxGCtr", "w" ); + } + fprintf(tracefile, "%s\n", buf ); + fflush(tracefile); +#else + PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("%s", buf)); +#endif +} +#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/src/libs/xpcom18a4/nsprpub/lib/msgc/src/prmsgc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/prmsgc.c new file mode 100644 index 00000000..ad0566a4 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/prmsgc.c @@ -0,0 +1,3514 @@ +/* -*- 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" + +#if defined(XP_MAC) +#include "pprthred.h" +#else +#include "private/pprthred.h" +#endif + +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 */ +#ifdef XP_MAC +#define MIN_FREE_THRESHOLD_AFTER_GC 10L +#else +#define MIN_FREE_THRESHOLD_AFTER_GC 20L +#endif + +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? +*/ +#if !defined(WIN16) +#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))) +#else + +#define SET_HBIT(_sp,_ph) set_hbit(_sp,_ph) + +#define CLEAR_HBIT(_sp,_ph) clear_hbit(_sp,_ph) + +#define IS_HBIT(_sp,_ph) is_hbit(_sp,_ph) + +static void +set_hbit(GCSeg *sp, PRWord *p) +{ + unsigned int distance; + unsigned int index; + PRWord mask; + + PR_ASSERT( SELECTOROF(p) == SELECTOROF(sp->base) ); + PR_ASSERT( OFFSETOF(p) >= OFFSETOF(sp->base) ); + + distance = (OFFSETOF(p) - OFFSETOF(sp->base)) >> 2; + index = distance >> PR_BITS_PER_WORD_LOG2; + mask = 1L << (distance&(PR_BITS_PER_WORD-1)); + + sp->hbits[index] |= mask; +} + +static void +clear_hbit(GCSeg *sp, PRWord *p) +{ + unsigned int distance; + unsigned int index; + PRWord mask; + + PR_ASSERT( SELECTOROF(p) == SELECTOROF(sp->base) ); + PR_ASSERT( OFFSETOF(p) >= OFFSETOF(sp->base) ); + + distance = (OFFSETOF(p) - OFFSETOF(sp->base)) >> 2; + index = distance >> PR_BITS_PER_WORD_LOG2; + mask = 1L << (distance&(PR_BITS_PER_WORD-1)); + + sp->hbits[index] &= ~mask; +} + +static int +is_hbit(GCSeg *sp, PRWord *p) +{ + unsigned int distance; + unsigned int index; + PRWord mask; + + PR_ASSERT( SELECTOROF(p) == SELECTOROF(sp->base) ); + PR_ASSERT( OFFSETOF(p) >= OFFSETOF(sp->base) ); + + distance = (OFFSETOF(p) - OFFSETOF(sp->base)) >> 2; + index = distance >> PR_BITS_PER_WORD_LOG2; + mask = 1L << (distance&(PR_BITS_PER_WORD-1)); + + return ((sp->hbits[index] & mask) != 0); +} + + +#endif /* WIN16 */ + +/* +** 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; +#if defined(WIN16) + PR_ASSERT( SELECTOROF(p) == SELECTOROF(base)); +#endif + 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 + +#if !defined(WIN16) +#define IN_SEGMENT(_sp, _p) \ + ((((char *)(_p)) >= (_sp)->base) && \ + (((char *)(_p)) < (_sp)->limit)) +#else +#define IN_SEGMENT(_sp, _p) \ + ((((PRWord)(_p)) >= ((PRWord)(_sp)->base)) && \ + (((PRWord)(_p)) < ((PRWord)(_sp)->limit))) +#endif + +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; + } + +#if defined(WIN16) + if (requestedSize > segmentSize) { + PR_DELETE(segInfo); + return 0; + } +#endif + + /* 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 */ +#if defined(WIN16) + PR_ASSERT( nhbytes < MAX_ALLOC_SIZE ); +#endif + 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++; + /* + ** XXX: + ** Until Win16 maintains lowSeg and highSeg correctly, + ** (ie. lowSeg=MIN(all segs) and highSeg = MAX(all segs)) + ** Allways scan through the segment list + */ +#if !defined(WIN16) + if (p0 < low) continue; /* below gc heap */ + if (p0 >= high) continue; /* above gc heap */ +#endif + /* 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; + + /* + ** XXX: + ** Until Win16 maintains lowSeg and highSeg correctly, + ** (ie. lowSeg=MIN(all segs) and highSeg = MAX(all segs)) + ** Allways scan through the segment list + */ +#if !defined(WIN16) + if (p0 < _pr_gcData.lowSeg) return; /* below gc heap */ + if (p0 >= _pr_gcData.highSeg) return; /* above gc heap */ +#endif + + /* 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) +{ +#ifdef XP_MAC +#pragma unused (notused) +#endif + 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) +{ +#ifdef XP_MAC +#pragma unused (unused) +#endif + 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) { +#ifdef XP_MAC +#pragma unused (notused) +#endif + 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; + + /* + ** XXX: + ** Until Win16 maintains lowSeg and highSeg correctly, + ** (ie. lowSeg=MIN(all segs) and highSeg = MAX(all segs)) + ** Allways scan through the segment list + */ +#if !defined(WIN16) + if (p0 < _pr_gcData.lowSeg) return 0; /* below gc heap */ + if (p0 >= _pr_gcData.highSeg) return 0; /* above gc heap */ +#endif + + /* 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) +{ +#if defined(XP_MAC) && XP_MAC +# pragma unused( detailed ) +#endif + + 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) +{ +#ifdef XP_MAC +#pragma unused(data) +#endif + 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 defined(XP_MAC) && XP_MAC +# pragma unused( out, detailed ) +#endif + + 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_CurrentThread()) < 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 + +#if defined(WIN16) + /* All memory MUST be aligned on 32bit boundaries */ + PR_ASSERT( (((PRWord)p) & (PR_BYTES_PER_WORD-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 :-( + */ +#if !defined(WIN16) + /* 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); +#else + /* + ** For WIN16 the shifts have been broken out into separate statements + ** to prevent the compiler from crashing... + */ + { + PRWord shiftVal; + + /* Check for possible overflow of bytes before performing add */ + if ((MAX_INT - PR_BYTES_PER_WORD) < bytes ) return NULL; + bytes += PR_BYTES_PER_WORD - 1L; + shiftVal = PR_BYTES_PER_WORD_LOG2; + bytes >>= shiftVal; + bytes <<= shiftVal; + /* Check for possible overflow of bytes before performing add */ + if ((MAX_INT - sizeof(PRWord)) < bytes ) return NULL; + bytes += sizeof(PRWord); + } +#endif + /* + * 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 :-( + */ +#if !defined(WIN16) + bytes = (bytes + PR_BYTES_PER_WORD - 1) >> PR_BYTES_PER_WORD_LOG2; + bytes <<= PR_BYTES_PER_WORD_LOG2; + bytes += sizeof(PRWord); +#else + /* + ** For WIN16 the shifts have been broken out into separate statements + ** to prevent the compiler from crashing... + */ + { + PRWord shiftVal; + + bytes += PR_BYTES_PER_WORD - 1L; + shiftVal = PR_BYTES_PER_WORD_LOG2; + bytes >>= shiftVal; + bytes <<= shiftVal; + bytes += sizeof(PRWord); + } +#endif + + /* + * 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 + +#if defined(WIN16) + PR_ASSERT( bytes < MAX_ALLOC_SIZE ); +#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(); + +#if defined(WIN16) + PR_ASSERT( initialHeapSize < MAX_ALLOC_SIZE ); +#endif + + /* 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); +} + +#if defined(WIN16) +/* +** For WIN16 the GC_IN_HEAP() macro must call the private InHeap function. +** This public wrapper function makes this possible... +*/ +PR_IMPLEMENT(PRBool) +PR_GC_In_Heap(void *object) +{ + return InHeap( object ) != NULL; +} +#endif + + +/** 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/src/libs/xpcom18a4/nsprpub/lib/msgc/src/unixgc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/unixgc.c new file mode 100644 index 00000000..cea4225e --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/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/src/libs/xpcom18a4/nsprpub/lib/msgc/src/win16gc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/win16gc.c new file mode 100644 index 00000000..0af0079b --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/win16gc.c @@ -0,0 +1,77 @@ +/* -*- 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 ***** */ +#if defined(WIN16) +#include <windows.h> +#endif +#include "prtypes.h" +#include <stdlib.h> + +#define MAX_SEGMENT_SIZE (65536l - 4096l) + +/************************************************************************/ +/* +** Machine dependent GC Heap management routines: +** _MD_GrowGCHeap +*/ +/************************************************************************/ + +void _MD_InitGC(void) {} + +extern void * +_MD_GrowGCHeap(PRUint32 *sizep) +{ + void *addr; + + if( *sizep > MAX_SEGMENT_SIZE ) { + *sizep = MAX_SEGMENT_SIZE; + } + + addr = malloc((size_t)*sizep); + return addr; +} + +HINSTANCE _pr_hInstance; + +int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, + WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + _pr_hInstance = hInst; + return TRUE; +} + + diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/src/win32gc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/src/win32gc.c new file mode 100644 index 00000000..eec83774 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/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/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/.cvsignore b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/Makefile.in b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/Makefile.in new file mode 100644 index 00000000..23518e15 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/Makefile.in @@ -0,0 +1,315 @@ +# +# ***** 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), WIN16) +OS_CFLAGS = $(OS_EXE_CFLAGS) +W16STDIO = $(MOD_DEPTH)/pr/src/md/windows/$(OBJDIR)/w16stdio.$(OBJ_SUFFIX) +endif + +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) +ifeq ($(OS_TARGET), WIN16) + LIBPR = $(dist_libdir)/nspr$(NSPR_VERSION).lib + LIBPLC = $(dist_libdir)/plc$(NSPR_VERSION).lib + LIBGC= $(dist_libdir)/msgc$(GC_VERSION).lib +else + LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -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 +endif + +ifeq ($(OS_ARCH),OS2) +ifeq ($(MOZ_OS2_TOOLS),VACPP) + LDOPTS = -NOE -DEBUG -nologo -PMTYPE:VIO + LIBPR = $(dist_libdir)/nspr$(NSPR_VERSION).lib + LIBPLC = $(dist_libdir)/plc$(NSPR_VERSION).lib + LIBGC= $(dist_libdir)/msgc$(GC_VERSION).lib +else + LDOPTS += -Zomf -Zlinker /PM:VIO +endif +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 + +ifneq ($(LOCAL_THREADS_ONLY),1) +# SunOS 5.4 and 5.5 need to link with -lthread or -lpthread, +# even though we already linked with these system libraries +# when we built libnspr.so. +ifeq ($(OS_RELEASE), 5.4) +EXTRA_LIBS = -lthread +endif + +ifeq ($(OS_RELEASE), 5.5) +ifdef USE_PTHREADS +EXTRA_LIBS = -lpthread +else +EXTRA_LIBS = -lthread +endif +endif +endif # LOCAL_THREADS_ONLY +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) +ifeq ($(OS_TARGET),WIN16) + echo system windows >w16link + echo name $@ >>w16link + echo option map >>w16link +# echo option CASEEXACT >>w16link + echo option stack=16K >>w16link + echo debug $(DEBUGTYPE) all >>w16link + echo file >>w16link + echo $< , >>w16link + echo $(W16STDIO) >>w16link + echo library >>w16link + echo $(LIBPR), >>w16link + echo $(LIBPLC), >>w16link + echo $(LIBGC), >>w16link + echo winsock.lib >>w16link + wlink @w16link. +else + 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) +export:: install +clean:: + rm -f $(TARGETS) diff --git a/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/gc1.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/gc1.c new file mode 100644 index 00000000..d6846235 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/gc1.c @@ -0,0 +1,257 @@ +/* -*- 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" +#ifndef XP_MAC +#include "private/pprthred.h" +#else +#include "pprthred.h" +#endif + +#include <stdio.h> +#include <stdlib.h> + +#ifdef XP_MAC +#include "prlog.h" +#define printf PR_LogPrint +extern void SetupMacPrintfLog(char *logFile); +#endif + +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(); + +#ifdef XP_MAC + SetupMacPrintfLog("gc1.log"); + debug_mode = 1; +#endif + + /* 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/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/thrashgc.c b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/thrashgc.c new file mode 100644 index 00000000..9d49ec19 --- /dev/null +++ b/src/libs/xpcom18a4/nsprpub/lib/msgc/tests/thrashgc.c @@ -0,0 +1,274 @@ +/* -*- 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" + +#ifndef XP_MAC +#include "private/pprthred.h" +#else +#include "pprthred.h" +#endif + +#include <stdio.h> +#include <memory.h> +#include <string.h> + + +#ifdef XP_MAC +#include "prlog.h" +#define printf PR_LogPrint +extern void SetupMacPrintfLog(char *logFile); +#endif + +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); +#ifndef XP_MAC + fprintf(stderr, "%s\n", msg); +#else + if (debug_mode) printf("%s\n", msg); +#endif + PR_Unlock(stderrLock); + } + +static void usage(char *progname) { +#ifndef XP_MAC + fprintf(stderr, "Usage: %s [-t threads] [-l loops]\n", progname); +#else + printf("Usage: %s [-t threads] [-l loops]\n", progname); +#endif + exit(-1); +} + +static int realMain(int argc, char **argv, char *notused) { + int i; + int threads = 0; + +#ifndef XP_MAC + 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); + } +#else + threads = 50; +#endif + + 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) { +#ifndef XP_MAC + fprintf(stderr, "%s: no more threads (only %d were created)\n", + progname, i); +#else + printf("%s: no more threads (only %d were created)\n", + progname, i); +#endif + 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(); + +#ifdef XP_MAC + SetupMacPrintfLog("thrashgc.log"); + debug_mode = 1; +#endif + + 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; +} |