/[jscoverage]/trunk/js/jsgc.cpp
ViewVC logotype

Diff of /trunk/js/jsgc.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 399 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC
# Line 1  Line 1 
1  /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-  /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2   * vim: set ts=8 sw=4 et tw=78:   * vim: set ts=8 sw=4 et tw=78:
3   *   *
4   * ***** BEGIN LICENSE BLOCK *****   * ***** BEGIN LICENSE BLOCK *****
# Line 48  Line 48 
48   *   *
49   * XXX swizzle page to freelist for better locality of reference   * XXX swizzle page to freelist for better locality of reference
50   */   */
 #include "jsstddef.h"  
51  #include <stdlib.h>     /* for free */  #include <stdlib.h>     /* for free */
52  #include <math.h>  #include <math.h>
53  #include <string.h>     /* for memset used when DEBUG */  #include <string.h>     /* for memset used when DEBUG */
54  #include "jstypes.h"  #include "jstypes.h"
55    #include "jsstdint.h"
56  #include "jsutil.h" /* Added by JSIFY */  #include "jsutil.h" /* Added by JSIFY */
57  #include "jshash.h" /* Added by JSIFY */  #include "jshash.h" /* Added by JSIFY */
58  #include "jsbit.h"  #include "jsbit.h"
# Line 74  Line 74 
74  #include "jsparse.h"  #include "jsparse.h"
75  #include "jsscope.h"  #include "jsscope.h"
76  #include "jsscript.h"  #include "jsscript.h"
77    #include "jsstaticcheck.h"
78  #include "jsstr.h"  #include "jsstr.h"
79    #include "jstask.h"
80  #include "jstracer.h"  #include "jstracer.h"
81    
82  #if JS_HAS_XML_SUPPORT  #if JS_HAS_XML_SUPPORT
83  #include "jsxml.h"  #include "jsxml.h"
84  #endif  #endif
85    
86    #ifdef INCLUDE_MOZILLA_DTRACE
87    #include "jsdtracef.h"
88    #endif
89    
90  /*  /*
91   * Check if posix_memalign is available.   * Check if posix_memalign is available.
92   */   */
# Line 109  Line 115 
115  #   define JS_GC_USE_MMAP 1  #   define JS_GC_USE_MMAP 1
116  #  endif  #  endif
117  #  include <windows.h>  #  include <windows.h>
118    # elif defined(__SYMBIAN32__)
119    // Symbian's OpenC has mmap (and #defines _POSIX_MAPPED_FILES), but
120    // doesn't implement MAP_ANON.  If we have MOZ_MEMORY, then we can use
121    // posix_memalign; we've defined HAS_POSIX_MEMALIGN above.  Otherwise,
122    // we overallocate.
123  # else  # else
124  #  if defined(XP_UNIX) || defined(XP_BEOS)  #  if defined(XP_UNIX) || defined(XP_BEOS)
125  #   include <unistd.h>  #   include <unistd.h>
# Line 123  Line 134 
134  #   if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)  #   if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
135  #    define MAP_ANONYMOUS MAP_ANON  #    define MAP_ANONYMOUS MAP_ANON
136  #   endif  #   endif
137    #   if !defined(MAP_ANONYMOUS)
138    #    define MAP_ANONYMOUS 0
139    #   endif
140  #  else  #  else
141  #   if JS_GC_USE_MMAP  #   if JS_GC_USE_MMAP
142  #    error "JS_GC_USE_MMAP is set when mmap is not available"  #    error "JS_GC_USE_MMAP is set when mmap is not available"
# Line 132  Line 146 
146  #endif  #endif
147    
148  /*  /*
149     * Check JSTempValueUnion has the size of jsval and void * so we can
150     * reinterpret jsval as void* GC-thing pointer and use JSTVU_SINGLE for
151     * different GC-things.
152     */
153    JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval));
154    JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(void *));
155    
156    /*
157     * Check that JSTRACE_XML follows JSTRACE_OBJECT, JSTRACE_DOUBLE and
158     * JSTRACE_STRING.
159     */
160    JS_STATIC_ASSERT(JSTRACE_OBJECT == 0);
161    JS_STATIC_ASSERT(JSTRACE_DOUBLE == 1);
162    JS_STATIC_ASSERT(JSTRACE_STRING == 2);
163    JS_STATIC_ASSERT(JSTRACE_XML    == 3);
164    
165    /*
166     * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_STRING is the last non-xml
167     * trace kind when JS_HAS_XML_SUPPORT is false.
168     */
169    JS_STATIC_ASSERT(JSTRACE_STRING + 1 == JSTRACE_XML);
170    
171    /*
172     * The number of used GCX-types must stay within GCX_LIMIT.
173     */
174    JS_STATIC_ASSERT(GCX_NTYPES <= GCX_LIMIT);
175    
176    
177    /*
178     * Check that we can reinterpret double as JSGCDoubleCell.
179     */
180    JS_STATIC_ASSERT(sizeof(JSGCDoubleCell) == sizeof(double));
181    
182    /*
183     * Check that we can use memset(p, 0, ...) to implement JS_CLEAR_WEAK_ROOTS.
184     */
185    JS_STATIC_ASSERT(JSVAL_NULL == 0);
186    
187    
188    /*
189   * A GC arena contains a fixed number of flag bits for each thing in its heap,   * A GC arena contains a fixed number of flag bits for each thing in its heap,
190   * and supports O(1) lookup of a flag given its thing's address.   * and supports O(1) lookup of a flag given its thing's address.
191   *   *
# Line 387  Line 441 
441      ((GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) / ((thingSize) + 1U))      ((GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) / ((thingSize) + 1U))
442    
443  #define THING_TO_ARENA(thing)                                                 \  #define THING_TO_ARENA(thing)                                                 \
444      ((JSGCArenaInfo *)(((jsuword) (thing) | GC_ARENA_MASK) +                  \      (JS_ASSERT(!JSString::isStatic(thing)),                                   \
445                         1 - sizeof(JSGCArenaInfo)))       (JSGCArenaInfo *)(((jsuword) (thing) | GC_ARENA_MASK)                    \
446                           + 1 - sizeof(JSGCArenaInfo)))
447    
448  #define THING_TO_INDEX(thing, thingSize)                                      \  #define THING_TO_INDEX(thing, thingSize)                                      \
449      ((uint32) ((jsuword) (thing) & GC_ARENA_MASK) / (uint32) (thingSize))      ((uint32) ((jsuword) (thing) & GC_ARENA_MASK) / (uint32) (thingSize))
# Line 597  Line 652 
652   * The maximum number of things to put on the local free list by taking   * The maximum number of things to put on the local free list by taking
653   * several things from the global free list or from the tail of the last   * several things from the global free list or from the tail of the last
654   * allocated arena to amortize the cost of rt->gcLock.   * allocated arena to amortize the cost of rt->gcLock.
  *  
  * We use number 8 based on benchmarks from bug 312238.  
655   */   */
656  #define MAX_THREAD_LOCAL_THINGS 8  #define MAX_THREAD_LOCAL_THINGS 64
657    
658  #endif  #endif
659    
# Line 670  Line 723 
723  {  {
724      if (table->array) {      if (table->array) {
725          JS_ASSERT(table->count > 0);          JS_ASSERT(table->count > 0);
726          free(table->array);          js_free(table->array);
727          table->array = NULL;          table->array = NULL;
728          table->count = 0;          table->count = 0;
729      }      }
# Line 704  Line 757 
757              if (capacity > (size_t)-1 / sizeof table->array[0])              if (capacity > (size_t)-1 / sizeof table->array[0])
758                  goto bad;                  goto bad;
759          }          }
760          array = (void **) realloc(table->array,          array = (void **) js_realloc(table->array,
761                                    capacity * sizeof table->array[0]);                                       capacity * sizeof table->array[0]);
762          if (!array)          if (!array)
763              goto bad;              goto bad;
764  #ifdef DEBUG  #ifdef DEBUG
# Line 744  Line 797 
797          array = table->array;          array = table->array;
798          JS_ASSERT(array);          JS_ASSERT(array);
799          if (capacity == 0) {          if (capacity == 0) {
800              free(array);              js_free(array);
801              table->array = NULL;              table->array = NULL;
802              return;              return;
803          }          }
804          array = (void **) realloc(array, capacity * sizeof array[0]);          array = (void **) js_realloc(array, capacity * sizeof array[0]);
805          if (array)          if (array)
806              table->array = array;              table->array = array;
807      }      }
# Line 829  Line 882 
882       *       *
883       * bytes to ensure that we always have room to store the gap.       * bytes to ensure that we always have room to store the gap.
884       */       */
885      p = malloc((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT);      p = js_malloc((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT);
886      if (!p)      if (!p)
887          return 0;          return 0;
888    
# Line 861  Line 914 
914  #endif  #endif
915    
916  #if HAS_POSIX_MEMALIGN  #if HAS_POSIX_MEMALIGN
917      free((void *) chunk);      js_free((void *) chunk);
918  #else  #else
919      /* See comments in NewGCChunk. */      /* See comments in NewGCChunk. */
920      JS_ASSERT(*GetMallocedChunkGapPtr(chunk) < GC_ARENA_SIZE);      JS_ASSERT(*GetMallocedChunkGapPtr(chunk) < GC_ARENA_SIZE);
921      free((void *) (chunk - *GetMallocedChunkGapPtr(chunk)));      js_free((void *) (chunk - *GetMallocedChunkGapPtr(chunk)));
922  #endif  #endif
923  }  }
924    
# Line 1040  Line 1093 
1093      for (i = 0; i < GC_NUM_FREELISTS; i++) {      for (i = 0; i < GC_NUM_FREELISTS; i++) {
1094          arenaList = &rt->gcArenaList[i];          arenaList = &rt->gcArenaList[i];
1095          thingSize = GC_FREELIST_NBYTES(i);          thingSize = GC_FREELIST_NBYTES(i);
         JS_ASSERT((size_t)(uint16)thingSize == thingSize);  
1096          arenaList->last = NULL;          arenaList->last = NULL;
1097          arenaList->lastCount = (uint16) THINGS_PER_ARENA(thingSize);          arenaList->lastCount = THINGS_PER_ARENA(thingSize);
1098          arenaList->thingSize = (uint16) thingSize;          arenaList->thingSize = thingSize;
1099          arenaList->freeList = NULL;          arenaList->freeList = NULL;
1100      }      }
1101      rt->gcDoubleArenaList.first = NULL;      rt->gcDoubleArenaList.first = NULL;
# Line 1094  Line 1146 
1146      JSGCArenaInfo *a;      JSGCArenaInfo *a;
1147      uint32 index;      uint32 index;
1148    
1149        if (JSString::isStatic(thing))
1150            return NULL;
1151      a = THING_TO_ARENA(thing);      a = THING_TO_ARENA(thing);
1152      if (!a->list)      if (!a->list)
1153          return NULL;          return NULL;
# Line 1104  Line 1158 
1158  intN  intN
1159  js_GetExternalStringGCType(JSString *str)  js_GetExternalStringGCType(JSString *str)
1160  {  {
1161      uintN type;      JS_ASSERT(!JSString::isStatic(str));
1162    
1163      type = (uintN) *GetGCThingFlags(str) & GCF_TYPEMASK;      uintN type = (uintN) *GetGCThingFlags(str) & GCF_TYPEMASK;
1164      JS_ASSERT(type == GCX_STRING || type >= GCX_EXTERNAL_STRING);      JS_ASSERT(type == GCX_STRING || type >= GCX_EXTERNAL_STRING);
1165      return (type == GCX_STRING) ? -1 : (intN) (type - GCX_EXTERNAL_STRING);      return (type == GCX_STRING) ? -1 : (intN) (type - GCX_EXTERNAL_STRING);
1166  }  }
# Line 1128  Line 1182 
1182      JSGCArenaInfo *a;      JSGCArenaInfo *a;
1183      uint32 index;      uint32 index;
1184    
1185        if (JSString::isStatic(thing))
1186            return JSTRACE_STRING;
1187    
1188      a = THING_TO_ARENA(thing);      a = THING_TO_ARENA(thing);
1189      if (!a->list)      if (!a->list)
1190          return JSTRACE_DOUBLE;          return JSTRACE_DOUBLE;
# Line 1155  Line 1212 
1212      JSGCArenaInfo *a;      JSGCArenaInfo *a;
1213      uint32 index, flags;      uint32 index, flags;
1214    
1215        if (JSString::isStatic(thing))
1216            return false;
1217    
1218      a = THING_TO_ARENA(thing);      a = THING_TO_ARENA(thing);
1219      if (!a->list) {      if (!a->list) {
1220          /*          /*
# Line 1253  Line 1313 
1313      rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;      rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
1314      rt->gcEmptyArenaPoolLifespan = 30000;      rt->gcEmptyArenaPoolLifespan = 30000;
1315    
1316        /*
1317         * By default the trigger factor gets maximum possible value. This
1318         * means that GC will not be triggered by growth of GC memory (gcBytes).
1319         */
1320        rt->setGCTriggerFactor((uint32) -1);
1321    
1322        /*
1323         * The assigned value prevents GC from running when GC memory is too low
1324         * (during JS engine start).
1325         */
1326        rt->setGCLastBytes(8192);
1327    
1328      METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));      METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));
1329      return JS_TRUE;      return JS_TRUE;
1330  }  }
# Line 1410  Line 1482 
1482  CheckLeakedRoots(JSRuntime *rt);  CheckLeakedRoots(JSRuntime *rt);
1483  #endif  #endif
1484    
 #ifdef JS_THREADSAFE  
 static void  
 TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount);  
 #endif  
   
1485  void  void
1486  js_FinishGC(JSRuntime *rt)  js_FinishGC(JSRuntime *rt)
1487  {  {
# Line 1426  Line 1493 
1493  #endif  #endif
1494    
1495      FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);      FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
 #ifdef JS_THREADSAFE  
     TrimGCFreeListsPool(rt, 0);  
     JS_ASSERT(!rt->gcFreeListsPool);  
 #endif  
1496      FinishGCArenaLists(rt);      FinishGCArenaLists(rt);
1497    
1498      if (rt->gcRootsHash.ops) {      if (rt->gcRootsHash.ops) {
# Line 1466  Line 1529 
1529       * properly with a racing GC, without calling JS_AddRoot from a request.       * properly with a racing GC, without calling JS_AddRoot from a request.
1530       * We have to preserve API compatibility here, now that we avoid holding       * We have to preserve API compatibility here, now that we avoid holding
1531       * rt->gcLock across the mark phase (including the root hashtable mark).       * rt->gcLock across the mark phase (including the root hashtable mark).
      *  
      * If the GC is running and we're called on another thread, wait for this  
      * GC activation to finish.  We can safely wait here (in the case where we  
      * are called within a request on another thread's context) without fear  
      * of deadlock because the GC doesn't set rt->gcRunning until after it has  
      * waited for all active requests to end.  
1532       */       */
1533      JS_LOCK_GC(rt);      JS_LOCK_GC(rt);
1534  #ifdef JS_THREADSAFE      js_WaitForGC(rt);
     JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);  
     if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {  
         do {  
             JS_AWAIT_GC_DONE(rt);  
         } while (rt->gcLevel > 0);  
     }  
 #endif  
1535      rhe = (JSGCRootHashEntry *)      rhe = (JSGCRootHashEntry *)
1536            JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD);            JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD);
1537      if (rhe) {      if (rhe) {
# Line 1503  Line 1553 
1553       * Same synchronization drill as above in js_AddRoot.       * Same synchronization drill as above in js_AddRoot.
1554       */       */
1555      JS_LOCK_GC(rt);      JS_LOCK_GC(rt);
1556  #ifdef JS_THREADSAFE      js_WaitForGC(rt);
     JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);  
     if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {  
         do {  
             JS_AWAIT_GC_DONE(rt);  
         } while (rt->gcLevel > 0);  
     }  
 #endif  
1557      (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE);      (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE);
1558      rt->gcPoke = JS_TRUE;      rt->gcPoke = JS_TRUE;
1559      JS_UNLOCK_GC(rt);      JS_UNLOCK_GC(rt);
# Line 1682  Line 1725 
1725  #define NGCHIST 64  #define NGCHIST 64
1726    
1727  static struct GCHist {  static struct GCHist {
1728      JSBool      lastDitch;      bool        lastDitch;
1729      JSGCThing   *freeList;      JSGCThing   *freeList;
1730  } gchist[NGCHIST];  } gchist[NGCHIST];
1731    
1732  unsigned gchpos = 0;  unsigned gchpos = 0;
1733  #endif  #endif
1734    
1735  #ifdef JS_THREADSAFE  void
1736    JSRuntime::setGCTriggerFactor(uint32 factor)
 const JSGCFreeListSet js_GCEmptyFreeListSet = {  
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, NULL  
 };  
   
 static void  
 TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount)  
1737  {  {
1738      JSGCFreeListSet **cursor, *freeLists, *link;      JS_ASSERT(factor >= 100);
1739    
1740      cursor = &rt->gcFreeListsPool;      gcTriggerFactor = factor;
1741      while (keepCount != 0) {      setGCLastBytes(gcLastBytes);
         --keepCount;  
         freeLists = *cursor;  
         if (!freeLists)  
             return;  
         memset(freeLists->array, 0, sizeof freeLists->array);  
         cursor = &freeLists->link;  
     }  
     freeLists = *cursor;  
     if (freeLists) {  
         *cursor = NULL;  
         do {  
             link = freeLists->link;  
             free(freeLists);  
         } while ((freeLists = link) != NULL);  
     }  
1742  }  }
1743    
1744  void  void
1745  js_RevokeGCLocalFreeLists(JSContext *cx)  JSRuntime::setGCLastBytes(size_t lastBytes)
1746  {  {
1747      JS_ASSERT(!cx->gcLocalFreeLists->link);      gcLastBytes = lastBytes;
1748      if (cx->gcLocalFreeLists != &js_GCEmptyFreeListSet) {      uint64 triggerBytes = uint64(lastBytes) * uint64(gcTriggerFactor / 100);
1749          cx->gcLocalFreeLists->link = cx->runtime->gcFreeListsPool;      if (triggerBytes != size_t(triggerBytes))
1750          cx->runtime->gcFreeListsPool = cx->gcLocalFreeLists;          triggerBytes = size_t(-1);
1751          cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;      gcTriggerBytes = size_t(triggerBytes);
     }  
1752  }  }
1753    
1754  static JSGCFreeListSet *  static JS_INLINE bool
1755  EnsureLocalFreeList(JSContext *cx)  IsGCThresholdReached(JSRuntime *rt)
1756  {  {
1757      JSGCFreeListSet *freeLists;  #ifdef JS_GC_ZEAL
1758        if (rt->gcZeal >= 1)
1759      freeLists = cx->gcLocalFreeLists;          return true;
1760      if (freeLists != &js_GCEmptyFreeListSet) {  #endif
         JS_ASSERT(freeLists);  
         return freeLists;  
     }  
1761    
1762      freeLists = cx->runtime->gcFreeListsPool;      /*
1763      if (freeLists) {       * Since the initial value of the gcLastBytes parameter is not equal to
1764          cx->runtime->gcFreeListsPool = freeLists->link;       * zero (see the js_InitGC function) the return value is false when
1765          freeLists->link = NULL;       * the gcBytes value is close to zero at the JS engine start.
1766      } else {       */
1767          /* JS_malloc is not used as the caller reports out-of-memory itself. */      return rt->gcMallocBytes >= rt->gcMaxMallocBytes ||
1768          freeLists = (JSGCFreeListSet *) calloc(1, sizeof *freeLists);             rt->gcBytes >= rt->gcTriggerBytes;
         if (!freeLists)  
             return NULL;  
     }  
     cx->gcLocalFreeLists = freeLists;  
     return freeLists;  
1769  }  }
1770    
1771  #endif  template <class T> static JS_INLINE T*
1772    NewGCThing(JSContext *cx, uintN flags)
 void *  
 js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)  
1773  {  {
1774      JSRuntime *rt;      JSRuntime *rt;
1775      uintN flindex;      bool doGC;
     JSBool doGC;  
1776      JSGCThing *thing;      JSGCThing *thing;
1777      uint8 *flagp;      uint8 *flagp;
1778      JSGCArenaList *arenaList;      JSGCArenaList *arenaList;
# Line 1775  Line 1785 
1785  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
1786      JSBool gcLocked;      JSBool gcLocked;
1787      uintN localMallocBytes;      uintN localMallocBytes;
     JSGCFreeListSet *freeLists;  
1788      JSGCThing **lastptr;      JSGCThing **lastptr;
1789      JSGCThing *tmpthing;      JSGCThing *tmpthing;
1790      uint8 *tmpflagp;      uint8 *tmpflagp;
# Line 1784  Line 1793 
1793    
1794      JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE);      JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE);
1795      rt = cx->runtime;      rt = cx->runtime;
1796      nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing));      size_t nbytes = sizeof(T);
1797      flindex = GC_FREELIST_INDEX(nbytes);      JS_ASSERT(JS_ROUNDUP(nbytes, sizeof(JSGCThing)) == nbytes);
1798        uintN flindex = GC_FREELIST_INDEX(nbytes);
1799    
1800      /* Updates of metering counters here may not be thread-safe. */      /* Updates of metering counters here may not be thread-safe. */
1801      METER(astats = &cx->runtime->gcStats.arenaStats[flindex]);      METER(astats = &cx->runtime->gcStats.arenaStats[flindex]);
# Line 1794  Line 1804 
1804  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
1805      gcLocked = JS_FALSE;      gcLocked = JS_FALSE;
1806      JS_ASSERT(cx->thread);      JS_ASSERT(cx->thread);
1807      freeLists = cx->gcLocalFreeLists;  
1808      thing = freeLists->array[flindex];      JSGCThing *&freeList = cx->thread->gcFreeLists[flindex];
1809      localMallocBytes = cx->thread->gcMallocBytes;      thing = freeList;
1810        localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes;
1811      if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {      if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
1812          flagp = thing->flagp;          flagp = thing->flagp;
1813          freeLists->array[flindex] = thing->next;          freeList = thing->next;
1814          METER(astats->localalloc++);          METER(astats->localalloc++);
1815          goto success;          goto success;
1816      }      }
# Line 1809  Line 1820 
1820    
1821      /* Transfer thread-local counter to global one. */      /* Transfer thread-local counter to global one. */
1822      if (localMallocBytes != 0) {      if (localMallocBytes != 0) {
1823          cx->thread->gcMallocBytes = 0;          JS_THREAD_DATA(cx)->gcMallocBytes = 0;
1824          if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes)          if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes)
1825              rt->gcMallocBytes = rt->gcMaxMallocBytes;              rt->gcMallocBytes = rt->gcMaxMallocBytes;
1826          else          else
# Line 1823  Line 1834 
1834          return NULL;          return NULL;
1835      }      }
1836    
1837      doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke);  #if defined JS_GC_ZEAL && defined JS_TRACER
1838  #ifdef JS_GC_ZEAL      if (rt->gcZeal >= 1 && JS_TRACE_MONITOR(cx).useReservedObjects)
1839      doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke);          goto testReservedObjects;
1840  #endif  #endif
1841    
1842      arenaList = &rt->gcArenaList[flindex];      arenaList = &rt->gcArenaList[flindex];
1843        doGC = IsGCThresholdReached(rt);
1844      for (;;) {      for (;;) {
1845          if (doGC && !JS_ON_TRACE(cx)) {          if (doGC
1846    #ifdef JS_TRACER
1847                && !JS_ON_TRACE(cx) && !JS_TRACE_MONITOR(cx).useReservedObjects
1848    #endif
1849                ) {
1850              /*              /*
1851               * Keep rt->gcLock across the call into js_GC so we don't starve               * Keep rt->gcLock across the call into js_GC so we don't starve
1852               * and lose to racing threads who deplete the heap just after               * and lose to racing threads who deplete the heap just after
# Line 1853  Line 1869 
1869  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
1870              /*              /*
1871               * Refill the local free list by taking several things from the               * Refill the local free list by taking several things from the
1872               * global free list unless we are still at rt->gcMaxMallocBytes               * global free list unless the free list is already populated or
1873               * barrier or the free list is already populated. The former               * we are still at rt->gcMaxMallocBytes barrier. The former is
1874               * happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN)               * caused via allocating new things in gcCallback(cx, JSGC_END).
1875               * or no gcPoke. The latter is caused via allocating new things               * The latter happens when GC is canceled due to
1876               * in gcCallback(cx, JSGC_END).               * gcCallback(cx, JSGC_BEGIN) returning false.
1877               */               */
1878              if (rt->gcMallocBytes >= rt->gcMaxMallocBytes)              if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
                 break;  
   
             freeLists = EnsureLocalFreeList(cx);  
             if (!freeLists)  
                 goto fail;  
             if (freeLists->array[flindex])  
1879                  break;                  break;
1880    
1881              tmpthing = arenaList->freeList;              tmpthing = arenaList->freeList;
# Line 1877  Line 1887 
1887                      tmpthing = tmpthing->next;                      tmpthing = tmpthing->next;
1888                  } while (--maxFreeThings != 0);                  } while (--maxFreeThings != 0);
1889    
1890                  freeLists->array[flindex] = arenaList->freeList;                  freeList = arenaList->freeList;
1891                  arenaList->freeList = tmpthing->next;                  arenaList->freeList = tmpthing->next;
1892                  tmpthing->next = NULL;                  tmpthing->next = NULL;
1893              }              }
# Line 1895  Line 1905 
1905              JS_ASSERT(arenaList->lastCount < thingsLimit);              JS_ASSERT(arenaList->lastCount < thingsLimit);
1906              a = arenaList->last;              a = arenaList->last;
1907          } else {          } else {
1908    #ifdef JS_TRACER
1909                if (JS_TRACE_MONITOR(cx).useReservedObjects) {
1910    #ifdef JS_GC_ZEAL
1911    testReservedObjects:
1912    #endif
1913                    JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
1914    
1915                    thing = (JSGCThing *) tm->reservedObjects;
1916                    flagp = GetGCThingFlags(thing);
1917                    JS_ASSERT(thing);
1918                    tm->reservedObjects = JSVAL_TO_OBJECT(tm->reservedObjects->fslots[0]);
1919                    break;
1920                }
1921    #endif
1922    
1923              a = NewGCArena(rt);              a = NewGCArena(rt);
1924              if (!a) {              if (!a) {
1925                  if (doGC || JS_ON_TRACE(cx))                  if (doGC || JS_ON_TRACE(cx))
1926                      goto fail;                      goto fail;
1927                  doGC = JS_TRUE;                  doGC = true;
1928                  continue;                  continue;
1929              }              }
1930              a->list = arenaList;              a->list = arenaList;
# Line 1920  Line 1945 
1945           * arena. Prefer to order free things by ascending address in the           * arena. Prefer to order free things by ascending address in the
1946           * (unscientific) hope of better cache locality.           * (unscientific) hope of better cache locality.
1947           */           */
1948          if (rt->gcMallocBytes >= rt->gcMaxMallocBytes)          if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
             break;  
   
         freeLists = EnsureLocalFreeList(cx);  
         if (!freeLists)  
             goto fail;  
         if (freeLists->array[flindex])  
1949              break;              break;
1950          lastptr = &freeLists->array[flindex];          lastptr = &freeList;
1951          maxFreeThings = thingsLimit - arenaList->lastCount;          maxFreeThings = thingsLimit - arenaList->lastCount;
1952          if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)          if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)
1953              maxFreeThings = MAX_THREAD_LOCAL_THINGS;              maxFreeThings = MAX_THREAD_LOCAL_THINGS;
1954            uint32 lastCount = arenaList->lastCount;
1955          while (maxFreeThings != 0) {          while (maxFreeThings != 0) {
1956              --maxFreeThings;              --maxFreeThings;
1957    
1958              tmpflagp = THING_FLAGP(a, arenaList->lastCount);              tmpflagp = THING_FLAGP(a, lastCount);
1959              tmpthing = FLAGP_TO_THING(tmpflagp, nbytes);              tmpthing = FLAGP_TO_THING(tmpflagp, nbytes);
1960              arenaList->lastCount++;              lastCount++;
1961              tmpthing->flagp = tmpflagp;              tmpthing->flagp = tmpflagp;
1962              *tmpflagp = GCF_FINAL;    /* signifying that thing is free */              *tmpflagp = GCF_FINAL;    /* signifying that thing is free */
1963    
# Line 1945  Line 1965 
1965              lastptr = &tmpthing->next;              lastptr = &tmpthing->next;
1966          }          }
1967          *lastptr = NULL;          *lastptr = NULL;
1968            arenaList->lastCount = lastCount;
1969  #endif  #endif
1970          break;          break;
1971      }      }
# Line 1997  Line 2018 
2018      if (gcLocked)      if (gcLocked)
2019          JS_UNLOCK_GC(rt);          JS_UNLOCK_GC(rt);
2020  #endif  #endif
2021      JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);      return (T*)thing;
     return thing;  
2022    
2023  fail:  fail:
2024  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
# Line 2006  Line 2026 
2026          JS_UNLOCK_GC(rt);          JS_UNLOCK_GC(rt);
2027  #endif  #endif
2028      METER(astats->fail++);      METER(astats->fail++);
2029      if (!JS_ON_TRACE(cx))      js_ReportOutOfMemory(cx);
         JS_ReportOutOfMemory(cx);  
2030      return NULL;      return NULL;
2031  }  }
2032    
2033    extern JSObject* js_NewGCObject(JSContext *cx, uintN flags)
2034    {
2035        return NewGCThing<JSObject>(cx, flags);
2036    }
2037    
2038    extern JSString* js_NewGCString(JSContext *cx, uintN flags)
2039    {
2040        return NewGCThing<JSString>(cx, flags);
2041    }
2042    
2043    extern JSFunction* js_NewGCFunction(JSContext *cx, uintN flags)
2044    {
2045        return NewGCThing<JSFunction>(cx, flags);
2046    }
2047    
2048    extern JSXML* js_NewGCXML(JSContext *cx, uintN flags)
2049    {
2050        return NewGCThing<JSXML>(cx, flags);
2051    }
2052    
2053  static JSGCDoubleCell *  static JSGCDoubleCell *
2054  RefillDoubleFreeList(JSContext *cx)  RefillDoubleFreeList(JSContext *cx)
2055  {  {
# Line 2033  Line 2072 
2072          return NULL;          return NULL;
2073      }      }
2074    
2075      if (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke      if (IsGCThresholdReached(rt))
 #ifdef JS_GC_ZEAL  
         && (rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke))  
 #endif  
         ) {  
2076          goto do_gc;          goto do_gc;
     }  
2077    
2078      /*      /*
2079       * Loop until we find a flag bitmap byte with unset bits indicating free       * Loop until we find a flag bitmap byte with unset bits indicating free
# Line 2058  Line 2092 
2092                      if (didGC || JS_ON_TRACE(cx)) {                      if (didGC || JS_ON_TRACE(cx)) {
2093                          METER(rt->gcStats.doubleArenaStats.fail++);                          METER(rt->gcStats.doubleArenaStats.fail++);
2094                          JS_UNLOCK_GC(rt);                          JS_UNLOCK_GC(rt);
2095                          if (!JS_ON_TRACE(cx))                          js_ReportOutOfMemory(cx);
                             JS_ReportOutOfMemory(cx);  
2096                          return NULL;                          return NULL;
2097                      }                      }
2098                      js_GC(cx, GC_LAST_DITCH);                      js_GC(cx, GC_LAST_DITCH);
# Line 2144  Line 2177 
2177          } while (bit != 0);          } while (bit != 0);
2178      }      }
2179      JS_ASSERT(list);      JS_ASSERT(list);
     JS_COUNT_OPERATION(cx, JSOW_ALLOCATION * JS_BITS_PER_WORD);  
2180    
2181      /*      /*
2182       * We delegate assigning cx->doubleFreeList to js_NewDoubleInRootedValue as       * We delegate assigning cx->doubleFreeList to js_NewDoubleInRootedValue as
# Line 2200  Line 2232 
2232      return dp;      return dp;
2233  }  }
2234    
2235    #ifdef JS_TRACER
2236  JSBool  JSBool
2237  js_AddAsGCBytes(JSContext *cx, size_t sz)  js_ReserveObjects(JSContext *cx, size_t nobjects)
2238  {  {
2239      JSRuntime *rt;      /*
2240         * Ensure at least nobjects objects are in the list. fslots[1] of each
2241      rt = cx->runtime;       * object on the reservedObjects list is the length of the list from there.
2242      if (rt->gcBytes >= rt->gcMaxBytes ||       */
2243          sz > (size_t) (rt->gcMaxBytes - rt->gcBytes)      JSObject *&head = JS_TRACE_MONITOR(cx).reservedObjects;
2244  #ifdef JS_GC_ZEAL      size_t i = head ? JSVAL_TO_INT(head->fslots[1]) : 0;
2245          || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke)      while (i < nobjects) {
2246  #endif          JSObject *obj = js_NewGCObject(cx, GCX_OBJECT);
2247          ) {          if (!obj)
         if (JS_ON_TRACE(cx)) {  
             JS_UNLOCK_GC(rt);  
             return JS_FALSE;  
         }  
         js_GC(cx, GC_LAST_DITCH);  
         if (rt->gcBytes >= rt->gcMaxBytes ||  
             sz > (size_t) (rt->gcMaxBytes - rt->gcBytes)) {  
             JS_UNLOCK_GC(rt);  
             JS_ReportOutOfMemory(cx);  
2248              return JS_FALSE;              return JS_FALSE;
2249          }          memset(obj, 0, sizeof(JSObject));
2250            /* The class must be set to something for finalization. */
2251            obj->classword = (jsuword) &js_ObjectClass;
2252            obj->fslots[0] = OBJECT_TO_JSVAL(head);
2253            i++;
2254            obj->fslots[1] = INT_TO_JSVAL(i);
2255            head = obj;
2256      }      }
     rt->gcBytes += (uint32) sz;  
     return JS_TRUE;  
 }  
2257    
2258  void      return JS_TRUE;
 js_RemoveAsGCBytes(JSRuntime *rt, size_t sz)  
 {  
     JS_ASSERT((size_t) rt->gcBytes >= sz);  
     rt->gcBytes -= (uint32) sz;  
2259  }  }
2260    #endif
2261    
2262  /*  /*
2263   * Shallow GC-things can be locked just by setting the GCF_LOCK bit, because   * Shallow GC-things can be locked just by setting the GCF_LOCK bit, because
# Line 2244  Line 2268 
2268      ((flagp) &&                                                               \      ((flagp) &&                                                               \
2269       ((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING ||                     \       ((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING ||                     \
2270        ((*(flagp) & GCF_TYPEMASK) == GCX_STRING &&                             \        ((*(flagp) & GCF_TYPEMASK) == GCX_STRING &&                             \
2271         !JSSTRING_IS_DEPENDENT((JSString *) (thing)))))         !((JSString *) (thing))->isDependent())))
2272    
2273  /* This is compatible with JSDHashEntryStub. */  /* This is compatible with JSDHashEntryStub. */
2274  typedef struct JSGCLockHashEntry {  typedef struct JSGCLockHashEntry {
# Line 2351  Line 2375 
2375  JS_PUBLIC_API(void)  JS_PUBLIC_API(void)
2376  JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)  JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
2377  {  {
     JSObject *obj;  
     size_t nslots, i;  
     jsval v;  
     JSString *str;  
   
2378      switch (kind) {      switch (kind) {
2379        case JSTRACE_OBJECT:        case JSTRACE_OBJECT: {
2380          /* If obj has no map, it must be a newborn. */          /* If obj has no map, it must be a newborn. */
2381          obj = (JSObject *) thing;          JSObject *obj = (JSObject *) thing;
2382          if (!obj->map)          if (!obj->map)
2383              break;              break;
2384          if (obj->map->ops->trace) {          obj->map->ops->trace(trc, obj);
             obj->map->ops->trace(trc, obj);  
         } else {  
             nslots = STOBJ_NSLOTS(obj);  
             for (i = 0; i != nslots; ++i) {  
                 v = STOBJ_GET_SLOT(obj, i);  
                 if (JSVAL_IS_TRACEABLE(v)) {  
                     JS_SET_TRACING_INDEX(trc, "slot", i);  
                     JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v),  
                                   JSVAL_TRACE_KIND(v));  
                 }  
             }  
         }  
2385          break;          break;
2386          }
2387    
2388        case JSTRACE_STRING:        case JSTRACE_STRING: {
2389          str = (JSString *)thing;          JSString *str = (JSString *) thing;
2390          if (JSSTRING_IS_DEPENDENT(str))          if (str->isDependent())
2391              JS_CALL_STRING_TRACER(trc, JSSTRDEP_BASE(str), "base");              JS_CALL_STRING_TRACER(trc, str->dependentBase(), "base");
2392          break;          break;
2393          }
2394    
2395  #if JS_HAS_XML_SUPPORT  #if JS_HAS_XML_SUPPORT
2396        case JSTRACE_XML:        case JSTRACE_XML:
# Line 2598  Line 2607 
2607    
2608        case JSTRACE_STRING:        case JSTRACE_STRING:
2609          for (;;) {          for (;;) {
2610                if (JSString::isStatic(thing))
2611                    goto out;
2612              flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing));              flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing));
2613              JS_ASSERT((*flagp & GCF_FINAL) == 0);              JS_ASSERT((*flagp & GCF_FINAL) == 0);
2614              JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp));              JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp));
2615              if (!JSSTRING_IS_DEPENDENT((JSString *) thing)) {              if (!((JSString *) thing)->isDependent()) {
2616                  *flagp |= GCF_MARK;                  *flagp |= GCF_MARK;
2617                  goto out;                  goto out;
2618              }              }
2619              if (*flagp & GCF_MARK)              if (*flagp & GCF_MARK)
2620                  goto out;                  goto out;
2621              *flagp |= GCF_MARK;              *flagp |= GCF_MARK;
2622              thing = JSSTRDEP_BASE((JSString *) thing);              thing = ((JSString *) thing)->dependentBase();
2623          }          }
2624          /* NOTREACHED */          /* NOTREACHED */
2625      }      }
# Line 2699  Line 2710 
2710      jsval *rp = (jsval *)rhe->root;      jsval *rp = (jsval *)rhe->root;
2711      jsval v = *rp;      jsval v = *rp;
2712    
2713      /* Ignore null object and scalar values. */      /* Ignore null reference, scalar values, and static strings. */
2714      if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) {      if (!JSVAL_IS_NULL(v) &&
2715            JSVAL_IS_GCTHING(v) &&
2716            !JSString::isStatic(JSVAL_TO_GCTHING(v))) {
2717  #ifdef DEBUG  #ifdef DEBUG
2718          JSBool root_points_to_gcArenaList = JS_FALSE;          JSBool root_points_to_gcArenaList = JS_FALSE;
2719          jsuword thing = (jsuword) JSVAL_TO_GCTHING(v);          jsuword thing = (jsuword) JSVAL_TO_GCTHING(v);
# Line 2786  Line 2799 
2799      if (fp->callobj)      if (fp->callobj)
2800          JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");          JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");
2801      if (fp->argsobj)      if (fp->argsobj)
2802          JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments");          JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(fp->argsobj), "arguments");
2803      if (fp->varobj)      if (fp->varobj)
2804          JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables");          JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables");
2805      if (fp->script) {      if (fp->script) {
2806          js_TraceScript(trc, fp->script);          js_TraceScript(trc, fp->script);
2807          if (fp->regs) {  
2808            /* fp->slots is null for watch pseudo-frames, see js_watch_set. */
2809            if (fp->slots) {
2810              /*              /*
2811               * Don't mark what has not been pushed yet, or what has been               * Don't mark what has not been pushed yet, or what has been
2812               * popped already.               * popped already.
2813               */               */
2814              nslots = (uintN) (fp->regs->sp - fp->slots);              if (fp->regs && fp->regs->sp) {
2815                    nslots = (uintN) (fp->regs->sp - fp->slots);
2816                    JS_ASSERT(nslots >= fp->script->nfixed);
2817                } else {
2818                    nslots = fp->script->nfixed;
2819                }
2820              TRACE_JSVALS(trc, nslots, fp->slots, "slot");              TRACE_JSVALS(trc, nslots, fp->slots, "slot");
2821          }          }
2822      } else {      } else {
# Line 2809  Line 2829 
2829                (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags)));                (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags)));
2830      JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this");      JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this");
2831    
     if (fp->callee)  
         JS_CALL_OBJECT_TRACER(trc, fp->callee, "callee");  
   
2832      if (fp->argv) {      if (fp->argv) {
2833            JS_CALL_VALUE_TRACER(trc, fp->argv[-2], "callee");
2834          nslots = fp->argc;          nslots = fp->argc;
2835          skip = 0;          skip = 0;
2836          if (fp->fun) {          if (fp->fun) {
# Line 2834  Line 2852 
2852          JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain");          JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain");
2853      if (fp->sharpArray)      if (fp->sharpArray)
2854          JS_CALL_OBJECT_TRACER(trc, fp->sharpArray, "sharp array");          JS_CALL_OBJECT_TRACER(trc, fp->sharpArray, "sharp array");
   
     if (fp->xmlNamespace)  
         JS_CALL_OBJECT_TRACER(trc, fp->xmlNamespace, "xmlNamespace");  
2855  }  }
2856    
2857  static void  static void
# Line 2874  Line 2889 
2889      js_CallValueTracerIfGCThing(trc, wr->lastInternalResult);      js_CallValueTracerIfGCThing(trc, wr->lastInternalResult);
2890  }  }
2891    
2892  JS_FRIEND_API(void)  JS_REQUIRES_STACK JS_FRIEND_API(void)
2893  js_TraceContext(JSTracer *trc, JSContext *acx)  js_TraceContext(JSTracer *trc, JSContext *acx)
2894  {  {
2895      JSStackFrame *fp, *nextChain;      JSStackFrame *fp, *nextChain;
# Line 2896  Line 2911 
2911              }                                                                 \              }                                                                 \
2912          JS_END_MACRO          JS_END_MACRO
2913    
 #ifdef JS_THREADSAFE  
         js_RevokeGCLocalFreeLists(acx);  
 #endif  
   
2914          /*          /*
2915           * Release the stackPool's arenas if the stackPool has existed for           * Release the stackPool's arenas if the stackPool has existed for
2916           * longer than the limit specified by gcEmptyArenaPoolLifespan.           * longer than the limit specified by gcEmptyArenaPoolLifespan.
# Line 2922  Line 2933 
2933       * Iterate frame chain and dormant chains.       * Iterate frame chain and dormant chains.
2934       *       *
2935       * (NB: see comment on this whole "dormant" thing in js_Execute.)       * (NB: see comment on this whole "dormant" thing in js_Execute.)
2936         *
2937         * Since js_GetTopStackFrame needs to dereference cx->thread to check for
2938         * JIT frames, we check for non-null thread here and avoid null checks
2939         * there. See bug 471197.
2940       */       */
2941      fp = acx->fp;  #ifdef JS_THREADSAFE
2942      nextChain = acx->dormantFrameChain;      if (acx->thread)
2943      if (!fp)  #endif
2944          goto next_chain;      {
2945            fp = js_GetTopStackFrame(acx);
2946            nextChain = acx->dormantFrameChain;
2947            if (!fp)
2948                goto next_chain;
2949    
2950      /* The top frame must not be dormant. */          /* The top frame must not be dormant. */
2951      JS_ASSERT(!fp->dormantNext);          JS_ASSERT(!fp->dormantNext);
2952      for (;;) {          for (;;) {
2953          do {              do {
2954              js_TraceStackFrame(trc, fp);                  js_TraceStackFrame(trc, fp);
2955          } while ((fp = fp->down) != NULL);              } while ((fp = fp->down) != NULL);
2956    
2957        next_chain:            next_chain:
2958          if (!nextChain)              if (!nextChain)
2959              break;                  break;
2960          fp = nextChain;              fp = nextChain;
2961          nextChain = nextChain->dormantNext;              nextChain = nextChain->dormantNext;
2962            }
2963      }      }
2964    
2965      /* Mark other roots-by-definition in acx. */      /* Mark other roots-by-definition in acx. */
2966      if (acx->globalObject)      if (acx->globalObject && !JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL))
2967          JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object");          JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object");
2968      TraceWeakRoots(trc, &acx->weakRoots);      TraceWeakRoots(trc, &acx->weakRoots);
2969      if (acx->throwing) {      if (acx->throwing) {
# Line 2976  Line 2996 
2996              tvr->u.trace(trc, tvr);              tvr->u.trace(trc, tvr);
2997              break;              break;
2998            case JSTVU_SPROP:            case JSTVU_SPROP:
2999              TRACE_SCOPE_PROPERTY(trc, tvr->u.sprop);              tvr->u.sprop->trace(trc);
3000              break;              break;
3001            case JSTVU_WEAK_ROOTS:            case JSTVU_WEAK_ROOTS:
3002              TraceWeakRoots(trc, tvr->u.weakRoots);              TraceWeakRoots(trc, tvr->u.weakRoots);
3003              break;              break;
3004            case JSTVU_PARSE_CONTEXT:            case JSTVU_COMPILER:
3005              js_TraceParseContext(trc, tvr->u.parseContext);              tvr->u.compiler->trace(trc);
3006              break;              break;
3007            case JSTVU_SCRIPT:            case JSTVU_SCRIPT:
3008              js_TraceScript(trc, tvr->u.script);              js_TraceScript(trc, tvr->u.script);
3009              break;              break;
3010              case JSTVU_ENUMERATOR:
3011                static_cast<JSAutoEnumStateRooter *>(tvr)->mark(trc);
3012                break;
3013            default:            default:
3014              JS_ASSERT(tvr->count >= 0);              JS_ASSERT(tvr->count >= 0);
3015              TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array");              TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array");
# Line 2995  Line 3018 
3018    
3019      if (acx->sharpObjectMap.depth > 0)      if (acx->sharpObjectMap.depth > 0)
3020          js_TraceSharpMap(trc, &acx->sharpObjectMap);          js_TraceSharpMap(trc, &acx->sharpObjectMap);
3021    
3022        js_TraceRegExpStatics(trc, acx);
3023    
3024    #ifdef JS_TRACER
3025        InterpState* state = acx->interpState;
3026        while (state) {
3027            if (state->nativeVp)
3028                TRACE_JSVALS(trc, state->nativeVpLen, state->nativeVp, "nativeVp");
3029            state = state->prev;
3030        }
3031    #endif
3032  }  }
3033    
3034  void  #ifdef JS_TRACER
3035  js_TraceTraceMonitor(JSTracer *trc, JSTraceMonitor *tm)  
3036    static void
3037    MarkReservedGCThings(JSTraceMonitor *tm)
3038  {  {
3039      if (IS_GC_MARKING_TRACER(trc)) {      /* Keep reserved doubles. */
3040          tm->recoveryDoublePoolPtr = tm->recoveryDoublePool;      for (jsval *ptr = tm->reservedDoublePool; ptr < tm->reservedDoublePoolPtr; ++ptr) {
3041          /* Make sure the global shape changes and will force a flush          jsdouble* dp = JSVAL_TO_DOUBLE(*ptr);
3042             of the code cache. */          JS_ASSERT(js_GetGCThingTraceKind(dp) == JSTRACE_DOUBLE);
3043          tm->globalShape = -1;  
3044            JSGCArenaInfo *a = THING_TO_ARENA(dp);
3045            JS_ASSERT(!a->list);
3046            if (!a->u.hasMarkedDoubles) {
3047                ClearDoubleArenaFlags(a);
3048                a->u.hasMarkedDoubles = JS_TRUE;
3049            }
3050            jsuint index = DOUBLE_THING_TO_INDEX(dp);
3051            JS_SET_BIT(DOUBLE_ARENA_BITMAP(a), index);
3052        }
3053        /* Keep reserved objects. */
3054        for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) {
3055            uint8 *flagp = GetGCThingFlags(obj);
3056            JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT);
3057            JS_ASSERT(*flagp != GCF_FINAL);
3058            *flagp |= GCF_MARK;
3059      }      }
3060  }  }
3061    
3062  void  #ifdef JS_THREADSAFE
3063    static JSDHashOperator
3064    reserved_gcthings_marker(JSDHashTable *table, JSDHashEntryHdr *hdr,
3065                             uint32, void *)
3066    {
3067        JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
3068    
3069        MarkReservedGCThings(&thread->data.traceMonitor);
3070        return JS_DHASH_NEXT;
3071    }
3072    #endif
3073    
3074    #endif
3075    
3076    JS_REQUIRES_STACK void
3077  js_TraceRuntime(JSTracer *trc, JSBool allAtoms)  js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
3078  {  {
3079      JSRuntime *rt = trc->context->runtime;      JSRuntime *rt = trc->context->runtime;
# Line 3018  Line 3083 
3083      if (rt->gcLocksHash)      if (rt->gcLocksHash)
3084          JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc);          JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc);
3085      js_TraceAtomState(trc, allAtoms);      js_TraceAtomState(trc, allAtoms);
     js_TraceNativeEnumerators(trc);  
3086      js_TraceRuntimeNumberState(trc);      js_TraceRuntimeNumberState(trc);
3087    
3088      iter = NULL;      iter = NULL;
3089      while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)      while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
3090          js_TraceContext(trc, acx);          js_TraceContext(trc, acx);
3091    
3092        js_TraceThreads(rt, trc);
3093    
3094      if (rt->gcExtraRootsTraceOp)      if (rt->gcExtraRootsTraceOp)
3095          rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);          rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
3096    
3097    #ifdef JS_TRACER
3098        for (int i = 0; i < JSBUILTIN_LIMIT; i++) {
3099            if (rt->builtinFunctions[i])
3100                JS_CALL_OBJECT_TRACER(trc, rt->builtinFunctions[i], "builtin function");
3101        }
3102    
3103        /* Mark reserved gcthings unless we are shutting down. */
3104        if (IS_GC_MARKING_TRACER(trc) && rt->state != JSRTS_LANDING) {
3105  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3106      /* Trace the loop table(s) which can contain pointers to code objects. */          JS_DHashTableEnumerate(&rt->threads, reserved_gcthings_marker, NULL);
    while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {  
        if (!acx->thread)  
            continue;  
        js_TraceTraceMonitor(trc, &acx->thread->traceMonitor);  
    }  
3107  #else  #else
3108     js_TraceTraceMonitor(trc, &rt->traceMonitor);          MarkReservedGCThings(&rt->threadData.traceMonitor);
3109    #endif
3110        }
3111    
3112    #endif
3113    }
3114    
3115    void
3116    js_TriggerGC(JSContext *cx, JSBool gcLocked)
3117    {
3118        JSRuntime *rt = cx->runtime;
3119    
3120    #ifdef JS_THREADSAFE
3121        JS_ASSERT(cx->requestDepth > 0);
3122  #endif  #endif
3123        JS_ASSERT(!rt->gcRunning);
3124        if (rt->gcIsNeeded)
3125            return;
3126    
3127        /*
3128         * Trigger the GC when it is safe to call an operation callback on any
3129         * thread.
3130         */
3131        rt->gcIsNeeded = JS_TRUE;
3132        js_TriggerAllOperationCallbacks(rt, gcLocked);
3133  }  }
3134    
3135  static void  static void
3136  ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)  ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
3137  {  {
3138      JSObject *obj, *pobj;      JSObject *obj = ssr->obj;
3139      uint32 slot;      JSObject *pobj = ssr->pobj;
3140        uint32 slot = ssr->slot;
     obj = ssr->obj;  
     pobj = ssr->pobj;  
     slot = ssr->slot;  
3141    
3142      while (pobj) {      while (pobj) {
3143          pobj = js_GetWrappedObject(cx, pobj);          pobj = js_GetWrappedObject(cx, pobj);
3144          if (pobj == obj) {          if (pobj == obj) {
3145              ssr->errnum = JSMSG_CYCLIC_VALUE;              ssr->cycle = true;
3146              return;              return;
3147          }          }
3148          pobj = JSVAL_TO_OBJECT(STOBJ_GET_SLOT(pobj, slot));          pobj = JSVAL_TO_OBJECT(STOBJ_GET_SLOT(pobj, slot));
3149      }      }
3150    
3151      pobj = ssr->pobj;      pobj = ssr->pobj;
3152        if (slot == JSSLOT_PROTO) {
3153            obj->setProto(pobj);
3154        } else {
3155            JS_ASSERT(slot == JSSLOT_PARENT);
3156            obj->setParent(pobj);
3157        }
3158    }
3159    
3160      if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {  void
3161          JSScope *scope, *newscope;  js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
3162          JSObject *oldproto;  {
3163        JSScript **listp, *script;
         /* Check to see whether obj shares its prototype's scope. */  
         scope = OBJ_SCOPE(obj);  
         oldproto = STOBJ_GET_PROTO(obj);  
         if (oldproto && OBJ_SCOPE(oldproto) == scope) {  
             /* Either obj needs a new empty scope, or it should share pobj's. */  
             if (!pobj ||  
                 !OBJ_IS_NATIVE(pobj) ||  
                 OBJ_GET_CLASS(cx, pobj) != STOBJ_GET_CLASS(oldproto)) {  
                 /*  
                  * With no proto and no scope of its own, obj is truly empty.  
                  *  
                  * If pobj is not native, obj needs its own empty scope -- it  
                  * should not continue to share oldproto's scope once oldproto  
                  * is not on obj's prototype chain.  That would put properties  
                  * from oldproto's scope ahead of properties defined by pobj,  
                  * in lookup order.  
                  *  
                  * If pobj's class differs from oldproto's, we may need a new  
                  * scope to handle differences in private and reserved slots,  
                  * so we suboptimally but safely make one.  
                  */  
                 if (!js_GetMutableScope(cx, obj)) {  
                     ssr->errnum = JSMSG_OUT_OF_MEMORY;  
                     return;  
                 }  
             } else if (OBJ_SCOPE(pobj) != scope) {  
                 newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);  
                 obj->map = &newscope->map;  
                 js_DropObjectMap(cx, &scope->map, obj);  
                 JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);  
             }  
         }  
3164    
3165          /*      for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) {
3166           * Regenerate property cache shape ids for all of the scopes along the          listp = &data->scriptsToGC[i];
3167           * old prototype chain, in case any property cache entries were filled          while ((script = *listp) != NULL) {
3168           * by looking up starting from obj.              *listp = script->u.nextToGC;
3169           */              script->u.nextToGC = NULL;
3170          while (oldproto && OBJ_IS_NATIVE(oldproto)) {              js_DestroyScript(cx, script);
             scope = OBJ_SCOPE(oldproto);  
             SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);  
             oldproto = STOBJ_GET_PROTO(scope->object);  
3171          }          }
3172      }      }
   
     /* Finally, do the deed. */  
     STOBJ_SET_SLOT(obj, slot, OBJECT_TO_JSVAL(pobj));  
3173  }  }
3174    
3175  static void  static void
3176  DestroyScriptsToGC(JSContext *cx, JSScript **listp)  FinalizeObject(JSContext *cx, JSObject *obj)
3177    {
3178        /* Cope with stillborn objects that have no map. */
3179        if (!obj->map)
3180            return;
3181    
3182        if (JS_UNLIKELY(cx->debugHooks->objectHook != NULL)) {
3183            cx->debugHooks->objectHook(cx, obj, JS_FALSE,
3184                                       cx->debugHooks->objectHookData);
3185        }
3186    
3187        /* Finalize obj first, in case it needs map and slots. */
3188        JSClass *clasp = STOBJ_GET_CLASS(obj);
3189        if (clasp->finalize)
3190            clasp->finalize(cx, obj);
3191    
3192    #ifdef INCLUDE_MOZILLA_DTRACE
3193        if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED())
3194            jsdtrace_object_finalize(obj);
3195    #endif
3196    
3197        if (JS_LIKELY(OBJ_IS_NATIVE(obj)))
3198            OBJ_SCOPE(obj)->drop(cx, obj);
3199        js_FreeSlots(cx, obj);
3200    }
3201    
3202    static JSStringFinalizeOp str_finalizers[GCX_NTYPES - GCX_EXTERNAL_STRING] = {
3203        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
3204    };
3205    
3206    intN
3207    js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
3208                                     JSStringFinalizeOp newop)
3209  {  {
3210      JSScript *script;      uintN i;
3211    
3212        for (i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) {
3213            if (str_finalizers[i] == oldop) {
3214                str_finalizers[i] = newop;
3215                return (intN) i;
3216            }
3217        }
3218        return -1;
3219    }
3220    
3221      while ((script = *listp) != NULL) {  /*
3222          *listp = script->u.nextToGC;   * cx is NULL when we are called from js_FinishAtomState to force the
3223          script->u.nextToGC = NULL;   * finalization of the permanently interned strings.
3224          js_DestroyScript(cx, script);   */
3225    void
3226    js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
3227    {
3228        jschar *chars;
3229        JSBool valid;
3230        JSStringFinalizeOp finalizer;
3231    
3232        JS_RUNTIME_UNMETER(rt, liveStrings);
3233        JS_ASSERT(!JSString::isStatic(str));
3234        if (str->isDependent()) {
3235            /* A dependent string can not be external and must be valid. */
3236            JS_ASSERT(type < 0);
3237            JS_ASSERT(str->dependentBase());
3238            JS_RUNTIME_UNMETER(rt, liveDependentStrings);
3239            valid = JS_TRUE;
3240        } else {
3241            /* A stillborn string has null chars, so is not valid. */
3242            chars = str->flatChars();
3243            valid = (chars != NULL);
3244            if (valid) {
3245                if (type < 0) {
3246                    if (cx)
3247                        cx->free(chars);
3248                    else
3249                        rt->free(chars);
3250                } else {
3251                    JS_ASSERT((uintN) type < JS_ARRAY_LENGTH(str_finalizers));
3252                    finalizer = str_finalizers[type];
3253                    if (finalizer) {
3254                        /*
3255                         * Assume that the finalizer for the permanently interned
3256                         * string knows how to deal with null context.
3257                         */
3258                        finalizer(cx, str);
3259                    }
3260                }
3261            }
3262      }      }
3263        if (valid && str->isDeflated())
3264            js_PurgeDeflatedStringCache(rt, str);
3265  }  }
3266    
3267  /*  /*
# Line 3146  Line 3284 
3284      JSBool allClear;      JSBool allClear;
3285  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3286      uint32 requestDebit;      uint32 requestDebit;
     JSContext *acx, *iter;  
3287  #endif  #endif
3288  #ifdef JS_GCMETER  #ifdef JS_GCMETER
3289      uint32 nlivearenas, nkilledarenas, nthings;      uint32 nlivearenas, nkilledarenas, nthings;
# Line 3154  Line 3291 
3291    
3292      JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx));      JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx));
3293      rt = cx->runtime;      rt = cx->runtime;
3294    
3295  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3296        /*
3297         * We allow js_GC calls outside a request but the context must be bound
3298         * to the current thread.
3299         */
3300        JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
3301    
3302      /* Avoid deadlock. */      /* Avoid deadlock. */
3303      JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));      JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));
3304  #endif  #endif
# Line 3215  Line 3359 
3359      rt->gcPoke = JS_FALSE;      rt->gcPoke = JS_FALSE;
3360    
3361  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3362      JS_ASSERT(cx->thread->id == js_CurrentThreadId());      /*
3363         * Check if the GC is already running on this or another thread and
3364         * delegate the job to it.
3365         */
3366        if (rt->gcLevel > 0) {
3367            JS_ASSERT(rt->gcThread);
3368    
3369      /* Bump gcLevel and return rather than nest on this thread. */          /* Bump gcLevel to restart the current GC, so it finds new garbage. */
     if (rt->gcThread == cx->thread) {  
         JS_ASSERT(rt->gcLevel > 0);  
3370          rt->gcLevel++;          rt->gcLevel++;
3371          METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel);          METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel);
         if (!(gckind & GC_LOCK_HELD))  
             JS_UNLOCK_GC(rt);  
         return;  
     }  
   
     /*  
      * If we're in one or more requests (possibly on more than one context)  
      * running on the current thread, indicate, temporarily, that all these  
      * requests are inactive.  If cx->thread is NULL, then cx is not using  
      * the request model, and does not contribute to rt->requestCount.  
      */  
     requestDebit = 0;  
     if (cx->thread) {  
         JSCList *head, *link;  
3372    
3373          /*          /*
3374           * Check all contexts on cx->thread->contextList for active requests,           * If the GC runs on another thread, temporarily suspend the current
3375           * counting each such context against requestDebit.           * request and wait until the GC is done.
3376           */           */
3377          head = &cx->thread->contextList;          if (rt->gcThread != cx->thread) {
3378          for (link = head->next; link != head; link = link->next) {              requestDebit = js_DiscountRequestsForGC(cx);
3379              acx = CX_FROM_THREAD_LINKS(link);              js_RecountRequestsAfterGC(rt, requestDebit);
             JS_ASSERT(acx->thread == cx->thread);  
             if (acx->requestDepth)  
                 requestDebit++;  
3380          }          }
     } else {  
         /*  
          * We assert, but check anyway, in case someone is misusing the API.  
          * Avoiding the loop over all of rt's contexts is a win in the event  
          * that the GC runs only on request-less contexts with null threads,  
          * in a special thread such as might be used by the UI/DOM/Layout  
          * "mozilla" or "main" thread in Mozilla-the-browser.  
          */  
         JS_ASSERT(cx->requestDepth == 0);  
         if (cx->requestDepth)  
             requestDebit = 1;  
     }  
     if (requestDebit) {  
         JS_ASSERT(requestDebit <= rt->requestCount);  
         rt->requestCount -= requestDebit;  
         if (rt->requestCount == 0)  
             JS_NOTIFY_REQUEST_DONE(rt);  
     }  
   
     /* If another thread is already in GC, don't attempt GC; wait instead. */  
     if (rt->gcLevel > 0) {  
         /* Bump gcLevel to restart the current GC, so it finds new garbage. */  
         rt->gcLevel++;  
         METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel);  
   
         /* Wait for the other thread to finish, then resume our request. */  
         while (rt->gcLevel > 0)  
             JS_AWAIT_GC_DONE(rt);  
         if (requestDebit)  
             rt->requestCount += requestDebit;  
3381          if (!(gckind & GC_LOCK_HELD))          if (!(gckind & GC_LOCK_HELD))
3382              JS_UNLOCK_GC(rt);              JS_UNLOCK_GC(rt);
3383          return;          return;
# Line 3287  Line 3387 
3387      rt->gcLevel = 1;      rt->gcLevel = 1;
3388      rt->gcThread = cx->thread;      rt->gcThread = cx->thread;
3389    
3390      /* Wait for all other requests to finish. */      /*
3391         * Notify all operation callbacks, which will give them a chance to
3392         * yield their current request. Contexts that are not currently
3393         * executing will perform their callback at some later point,
3394         * which then will be unnecessary, but harmless.
3395         */
3396        js_NudgeOtherContexts(cx);
3397    
3398        /*
3399         * Discount all the requests on the current thread from contributing
3400         * to rt->requestCount before we wait for all other requests to finish.
3401         * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on
3402         * rt->requestCount transitions to 0.
3403         */
3404        requestDebit = js_CountThreadRequests(cx);
3405        JS_ASSERT_IF(cx->requestDepth != 0, requestDebit >= 1);
3406        rt->requestCount -= requestDebit;
3407      while (rt->requestCount > 0)      while (rt->requestCount > 0)
3408          JS_AWAIT_REQUEST_DONE(rt);          JS_AWAIT_REQUEST_DONE(rt);
3409        rt->requestCount += requestDebit;
3410    
3411  #else  /* !JS_THREADSAFE */  #else  /* !JS_THREADSAFE */
3412    
# Line 3329  Line 3446 
3446           * collect garbage only if a racing thread attempted GC and is waiting           * collect garbage only if a racing thread attempted GC and is waiting
3447           * for us to finish (gcLevel > 1) or if someone already poked us.           * for us to finish (gcLevel > 1) or if someone already poked us.
3448           */           */
3449          if (rt->gcLevel == 1 && !rt->gcPoke)          if (rt->gcLevel == 1 && !rt->gcPoke && !rt->gcIsNeeded)
3450              goto done_running;              goto done_running;
3451    
3452          rt->gcLevel = 0;          rt->gcLevel = 0;
# Line 3337  Line 3454 
3454          rt->gcRunning = JS_FALSE;          rt->gcRunning = JS_FALSE;
3455  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3456          rt->gcThread = NULL;          rt->gcThread = NULL;
         rt->requestCount += requestDebit;  
3457  #endif  #endif
3458          gckind = GC_LOCK_HELD;          gckind = GC_LOCK_HELD;
3459          goto restart_at_beginning;          goto restart_at_beginning;
# Line 3349  Line 3465 
3465      if (JS_ON_TRACE(cx))      if (JS_ON_TRACE(cx))
3466          goto out;          goto out;
3467  #endif  #endif
3468        VOUCH_HAVE_STACK();
3469    
3470        /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
3471        rt->gcIsNeeded = JS_FALSE;
3472    
3473      /* Reset malloc counter. */      /* Reset malloc counter. */
3474      rt->gcMallocBytes = 0;      rt->gcMallocBytes = 0;
# Line 3359  Line 3479 
3479    }    }
3480  #endif  #endif
3481    
     /* Clear property and JIT oracle caches (only for cx->thread if JS_THREADSAFE). */  
     js_FlushPropertyCache(cx);  
3482  #ifdef JS_TRACER  #ifdef JS_TRACER
3483      js_FlushJITOracle(cx);      js_PurgeJITOracle();
3484  #endif  #endif
3485    
3486      /* Destroy eval'ed scripts. */    restart:
3487      DestroyScriptsToGC(cx, &JS_SCRIPTS_TO_GC(cx));      rt->gcNumber++;
3488        JS_ASSERT(!rt->gcUntracedArenaStackTop);
3489        JS_ASSERT(rt->gcTraceLaterCount == 0);
3490    
 #ifdef JS_THREADSAFE  
3491      /*      /*
3492       * Clear thread-based caches. To avoid redundant clearing we unroll the       * Reset the property cache's type id generator so we can compress ids.
3493       * current thread's step.       * Same for the protoHazardShape proxy-shape standing in for all object
3494       *       * prototypes having readonly or setter properties.
      * In case a JSScript wrapped within an object was finalized, we null  
      * acx->thread->gsnCache.script and finish the cache's hashtable. Note  
      * that js_DestroyScript, called from script_finalize, will have already  
      * cleared cx->thread->gsnCache above during finalization, so we don't  
      * have to here.  
3495       */       */
3496      iter = NULL;      if (rt->shapeGen & SHAPE_OVERFLOW_BIT
3497      while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {  #ifdef JS_GC_ZEAL
3498          if (!acx->thread || acx->thread == cx->thread)          || rt->gcZeal >= 1
             continue;  
         GSN_CACHE_CLEAR(&acx->thread->gsnCache);  
         js_FlushPropertyCache(acx);  
 #ifdef JS_TRACER  
         js_FlushJITOracle(acx);  
3499  #endif  #endif
3500          DestroyScriptsToGC(cx, &acx->thread->scriptsToGC);          ) {
3501            rt->gcRegenShapes = true;
3502            rt->gcRegenShapesScopeFlag ^= JSScope::SHAPE_REGEN;
3503            rt->shapeGen = 0;
3504            rt->protoHazardShape = 0;
3505      }      }
 #else  
     /* The thread-unsafe case just has to clear the runtime's GSN cache. */  
     GSN_CACHE_CLEAR(&rt->gsnCache);  
 #endif  
   
   restart:  
     rt->gcNumber++;  
     JS_ASSERT(!rt->gcUntracedArenaStackTop);  
     JS_ASSERT(rt->gcTraceLaterCount == 0);  
3506    
3507      /* Reset the property cache's type id generator so we can compress ids. */      js_PurgeThreads(cx);
3508      rt->shapeGen = 0;  #ifdef JS_TRACER
3509        if (gckind == GC_LAST_CONTEXT) {
3510            /* Clear builtin functions, which are recreated on demand. */
3511            memset(rt->builtinFunctions, 0, sizeof rt->builtinFunctions);
3512        }
3513    #endif
3514    
3515      /*      /*
3516       * Mark phase.       * Mark phase.
# Line 3433  Line 3542 
3542    
3543      rt->gcMarkingTracer = NULL;      rt->gcMarkingTracer = NULL;
3544    
3545    #ifdef JS_THREADSAFE
3546        cx->createDeallocatorTask();
3547    #endif
3548    
3549      /*      /*
3550       * Sweep phase.       * Sweep phase.
3551       *       *
# Line 3506  Line 3619 
3619                          type = flags & GCF_TYPEMASK;                          type = flags & GCF_TYPEMASK;
3620                          switch (type) {                          switch (type) {
3621                            case GCX_OBJECT:                            case GCX_OBJECT:
3622                              js_FinalizeObject(cx, (JSObject *) thing);                              FinalizeObject(cx, (JSObject *) thing);
                             break;  
                           case GCX_DOUBLE:  
                             /* Do nothing. */  
3623                              break;                              break;
3624  #if JS_HAS_XML_SUPPORT  #if JS_HAS_XML_SUPPORT
3625                            case GCX_XML:                            case GCX_XML:
# Line 3543  Line 3653 
3653                   */                   */
3654                  freeList = arenaList->freeList;                  freeList = arenaList->freeList;
3655                  if (a == arenaList->last)                  if (a == arenaList->last)
3656                      arenaList->lastCount = (uint16) indexLimit;                      arenaList->lastCount = indexLimit;
3657                  *ap = a->prev;                  *ap = a->prev;
3658                  a->prev = emptyArenas;                  a->prev = emptyArenas;
3659                  emptyArenas = a;                  emptyArenas = a;
# Line 3567  Line 3677 
3677                                 nlivearenas, nkilledarenas, nthings));                                 nlivearenas, nkilledarenas, nthings));
3678      }      }
3679    
 #ifdef JS_THREADSAFE  
     /*  
      * Release all but two free list sets to avoid allocating a new set in  
      * js_NewGCThing.  
      */  
     TrimGCFreeListsPool(rt, 2);  
 #endif  
   
3680      ap = &rt->gcDoubleArenaList.first;      ap = &rt->gcDoubleArenaList.first;
3681      METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));      METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
3682      while ((a = *ap) != NULL) {      while ((a = *ap) != NULL) {
# Line 3622  Line 3724 
3724       */       */
3725      DestroyGCArenas(rt, emptyArenas);      DestroyGCArenas(rt, emptyArenas);
3726    
3727    #ifdef JS_THREADSAFE
3728        cx->submitDeallocatorTask();
3729    #endif
3730    
3731      if (rt->gcCallback)      if (rt->gcCallback)
3732          (void) rt->gcCallback(cx, JSGC_FINALIZE_END);          (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
3733  #ifdef DEBUG_srcnotesize  #ifdef DEBUG_srcnotesize
# Line 3668  Line 3774 
3774       * We want to restart GC if js_GC was called recursively or if any of the       * We want to restart GC if js_GC was called recursively or if any of the
3775       * finalizers called js_RemoveRoot or js_UnlockGCThingRT.       * finalizers called js_RemoveRoot or js_UnlockGCThingRT.
3776       */       */
3777      if (rt->gcLevel > 1 || rt->gcPoke) {      if (!JS_ON_TRACE(cx) && (rt->gcLevel > 1 || rt->gcPoke)) {
3778            VOUCH_HAVE_STACK();
3779          rt->gcLevel = 1;          rt->gcLevel = 1;
3780          rt->gcPoke = JS_FALSE;          rt->gcPoke = JS_FALSE;
3781          JS_UNLOCK_GC(rt);          JS_UNLOCK_GC(rt);
3782          goto restart;          goto restart;
3783      }      }
3784    
3785      if (rt->shapeGen >= SHAPE_OVERFLOW_BIT - 1) {      rt->setGCLastBytes(rt->gcBytes);
         /*  
          * FIXME bug 440834: The shape id space has overflowed. Currently we  
          * cope badly with this. Every call to js_GenerateShape does GC, and  
          * we never re-enable the property cache.  
          */  
         js_DisablePropertyCache(cx);  
 #ifdef JS_THREADSAFE  
         iter = NULL;  
         while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {  
             if (!acx->thread || acx->thread == cx->thread)  
                 continue;  
             js_DisablePropertyCache(acx);  
         }  
 #endif  
     }  
   
     rt->gcLastBytes = rt->gcBytes;  
3786    done_running:    done_running:
3787      rt->gcLevel = 0;      rt->gcLevel = 0;
3788      rt->gcRunning = JS_FALSE;      rt->gcRunning = rt->gcRegenShapes = false;
3789    
3790  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
     /* If we were invoked during a request, pay back the temporary debit. */  
     if (requestDebit)  
         rt->requestCount += requestDebit;  
3791      rt->gcThread = NULL;      rt->gcThread = NULL;
3792      JS_NOTIFY_GC_DONE(rt);      JS_NOTIFY_GC_DONE(rt);
3793    
# Line 3747  Line 3834 
3834          }          }
3835      }      }
3836  }  }
   
 void  
 js_UpdateMallocCounter(JSContext *cx, size_t nbytes)  
 {  
     uint32 *pbytes, bytes;  
   
 #ifdef JS_THREADSAFE  
     pbytes = &cx->thread->gcMallocBytes;  
 #else  
     pbytes = &cx->runtime->gcMallocBytes;  
 #endif  
     bytes = *pbytes;  
     *pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes;  
 }  

Legend:
Removed from v.399  
changed lines
  Added in v.507

  ViewVC Help
Powered by ViewVC 1.1.24