home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsgc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  18.1 KB  |  687 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS Mark-and-Sweep Garbage Collector.
  21.  *
  22.  * This GC allocates only fixed-sized things big enough to contain two words
  23.  * (pointers) on any host architecture.  It allocates from an arena pool (see
  24.  * prarena.h).  It uses a parallel arena-pool array of flag bytes to hold the
  25.  * mark bit, finalizer type index, etc.
  26.  *
  27.  * XXX swizzle page to freelist for better locality of reference
  28.  */
  29. #include <stdlib.h>     /* for free, called by PR_ARENA_DESTROY */
  30. #include <string.h>    /* for memset, called by prarena.h macros if DEBUG */
  31. #include "prtypes.h"
  32. #ifndef NSPR20
  33. #include "prarena.h"
  34. #else
  35. #include "plarena.h"
  36. #endif
  37. #include "prlog.h"
  38. #ifndef NSPR20
  39. #include "prhash.h"
  40. #else
  41. #include "plhash.h"
  42. #endif
  43. #include "jsapi.h"
  44. #include "jsatom.h"
  45. #include "jscntxt.h"
  46. #include "jsgc.h"
  47. #include "jsinterp.h"
  48. #include "jslock.h"
  49. #include "jsnum.h"
  50. #include "jsobj.h"
  51. #include "jsscope.h"
  52. #include "jsstr.h"
  53.  
  54. /*
  55.  * Arena sizes, the first must be a multiple of the second so the two arena
  56.  * pools can be maintained (in particular, arenas may be destroyed from the
  57.  * middle of each pool) in parallel.
  58.  */
  59. #define GC_ARENA_SIZE    8192        /* 1024 (512 on Alpha) objects */
  60. #define GC_FLAGS_SIZE    (GC_ARENA_SIZE / sizeof(JSGCThing))
  61. #define GC_ROOTS_SIZE    256        /* SWAG, small enough to amortize */
  62.  
  63. static PRHashNumber   gc_hash_root(const void *key);
  64.  
  65. struct JSGCThing {
  66.     JSGCThing       *next;
  67.     uint8           *flagp;
  68. };
  69.  
  70. typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing);
  71.  
  72. static GCFinalizeOp gc_finalizers[GCX_NTYPES];
  73.  
  74. #ifdef JS_GCMETER
  75. #define METER(x) x
  76. #else
  77. #define METER(x) /* nothing */
  78. #endif
  79.  
  80. JSBool
  81. js_InitGC(JSRuntime *rt, uint32 maxbytes)
  82. {
  83.     if (!gc_finalizers[GCX_OBJECT]) {
  84.     gc_finalizers[GCX_OBJECT] = (GCFinalizeOp)js_FinalizeObject;
  85.     gc_finalizers[GCX_STRING] = (GCFinalizeOp)js_FinalizeString;
  86.     gc_finalizers[GCX_DOUBLE] = (GCFinalizeOp)js_FinalizeDouble;
  87.     }
  88.  
  89.     PR_InitArenaPool(&rt->gcArenaPool, "gc-arena", GC_ARENA_SIZE,
  90.              sizeof(JSGCThing));
  91.     PR_InitArenaPool(&rt->gcFlagsPool, "gc-flags", GC_FLAGS_SIZE,
  92.              sizeof(uint8));
  93.     rt->gcRootsHash = PR_NewHashTable(GC_ROOTS_SIZE, gc_hash_root,
  94.                       PR_CompareValues, PR_CompareValues,
  95.                       NULL, NULL);
  96.     if (!rt->gcRootsHash)
  97.     return JS_FALSE;
  98.     rt->gcMaxBytes = maxbytes;
  99.     return JS_TRUE;
  100. }
  101.  
  102. #ifdef JS_GCMETER
  103. void
  104. js_DumpGCStats(JSRuntime *rt, FILE *fp)
  105. {
  106.     fprintf(fp, "\nGC allocation statistics:\n");
  107.     fprintf(fp, "     bytes currently allocated: %lu\n", rt->gcBytes);
  108.     fprintf(fp, "                alloc attempts: %lu\n", rt->gcStats.alloc);
  109.     fprintf(fp, "            GC freelist length: %lu\n", rt->gcStats.freelen);
  110.     fprintf(fp, "  recycles through GC freelist: %lu\n", rt->gcStats.recycle);
  111.     fprintf(fp, "alloc retries after running GC: %lu\n", rt->gcStats.retry);
  112.     fprintf(fp, "           allocation failures: %lu\n", rt->gcStats.fail);
  113.     fprintf(fp, "              valid lock calls: %lu\n", rt->gcStats.lock);
  114.     fprintf(fp, "            valid unlock calls: %lu\n", rt->gcStats.unlock);
  115.     fprintf(fp, "   locks that hit stuck counts: %lu\n", rt->gcStats.stuck);
  116.     fprintf(fp, " unlocks that saw stuck counts: %lu\n", rt->gcStats.unstuck);
  117.     fprintf(fp, "          mark recursion depth: %lu\n", rt->gcStats.depth);
  118.     fprintf(fp, "  maximum mark recursion depth: %lu\n", rt->gcStats.maxdepth);
  119.     fprintf(fp, "      maximum GC nesting level: %lu\n", rt->gcStats.maxlevel);
  120.     fprintf(fp, "   potentially useful GC calls: %lu\n", rt->gcStats.poke);
  121.     fprintf(fp, "              useless GC calls: %lu\n", rt->gcStats.nopoke);
  122.     fprintf(fp, "        thing arena corruption: %lu\n", rt->gcStats.badarena);
  123.     fprintf(fp, "        flags arena corruption: %lu\n", rt->gcStats.badflag);
  124.     fprintf(fp, "     thing arenas freed so far: %lu\n", rt->gcStats.afree);
  125.     fprintf(fp, "     flags arenas freed so far: %lu\n", rt->gcStats.fafree);
  126. #ifdef PR_ARENAMETER
  127.     PR_DumpArenaStats(fp);
  128. #endif
  129. }
  130. #endif
  131.  
  132. void
  133. js_FinishGC(JSRuntime *rt)
  134. {
  135. #ifdef PR_ARENAMETER
  136.     PR_DumpArenaStats(stdout);
  137. #endif
  138. #ifdef JS_GCMETER
  139.     js_DumpGCStats(rt, stdout);
  140. #endif
  141.     PR_FinishArenaPool(&rt->gcArenaPool);
  142.     PR_FinishArenaPool(&rt->gcFlagsPool);
  143.     PR_ArenaFinish();
  144.     PR_HashTableDestroy(rt->gcRootsHash);
  145.     rt->gcRootsHash = NULL;
  146.     rt->gcFreeList = NULL;
  147. }
  148.  
  149. JSBool
  150. js_AddRoot(JSContext *cx, void *rp)
  151. {
  152.     if (!PR_HashTableAdd(cx->runtime->gcRootsHash, rp, NULL)) {
  153.     JS_ReportOutOfMemory(cx);
  154.     return JS_FALSE;
  155.     }
  156.     return JS_TRUE;
  157. }
  158.  
  159. JSBool
  160. js_AddNamedRoot(JSContext *cx, void *rp, const char *name)
  161. {
  162.     if (!PR_HashTableAdd(cx->runtime->gcRootsHash, rp, (void *)name)) {
  163.     JS_ReportOutOfMemory(cx);
  164.     return JS_FALSE;
  165.     }
  166.     return JS_TRUE;
  167. }
  168.  
  169. JSBool
  170. js_RemoveRoot(JSContext *cx, void *rp)
  171. {
  172.     (void) PR_HashTableRemove(cx->runtime->gcRootsHash, rp);
  173.     return JS_TRUE;
  174. }
  175.  
  176. void *
  177. js_AllocGCThing(JSContext *cx, uintN flags)
  178. {
  179.     JSRuntime *rt;
  180.     JSGCThing *thing;
  181.     uint8 *flagp;
  182. #ifdef TOO_MUCH_GC
  183.     JSBool tried_gc = JS_TRUE;
  184.     js_GC(cx);
  185. #else
  186.     JSBool tried_gc = JS_FALSE;
  187. #endif
  188.  
  189.     rt = cx->runtime;
  190.     JS_LOCK_RUNTIME(rt);
  191.     METER(rt->gcStats.alloc++);
  192. retry:
  193.     thing = rt->gcFreeList;
  194.     if (thing) {
  195.     rt->gcFreeList = thing->next;
  196.     flagp = thing->flagp;
  197.     METER(rt->gcStats.freelen--);
  198.     METER(rt->gcStats.recycle++);
  199.     } else {
  200.     if (rt->gcBytes < rt->gcMaxBytes) {
  201.         PR_ARENA_ALLOCATE(thing, &rt->gcArenaPool, sizeof(JSGCThing));
  202.         PR_ARENA_ALLOCATE(flagp, &rt->gcFlagsPool, sizeof(uint8));
  203.     }
  204.     if (!thing || !flagp) {
  205.         if (thing)
  206.         PR_ARENA_RELEASE(&rt->gcArenaPool, thing);
  207.         if (!tried_gc) {
  208.         js_GC(cx);
  209.         tried_gc = JS_TRUE;
  210.         METER(rt->gcStats.retry++);
  211.         goto retry;
  212.         }
  213.         JS_ReportOutOfMemory(cx);
  214.         METER(rt->gcStats.fail++);
  215.         JS_UNLOCK_RUNTIME(rt);
  216.         return NULL;
  217.     }
  218.     }
  219.     *flagp = (uint8)flags;
  220.     rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8);
  221.     cx->newborn[flags & GCF_TYPEMASK] = thing;
  222.  
  223.     /*
  224.      * Clear thing before unlocking in case a GC run is about to scan it,
  225.      * finding it via cx->newborn[].
  226.      */
  227.     thing->next = NULL;
  228.     thing->flagp = NULL;
  229.     JS_UNLOCK_RUNTIME(rt);
  230.     return thing;
  231. }
  232.  
  233. static uint8 *
  234. gc_find_flags(JSRuntime *rt, void *thing)
  235. {
  236.     pruword index, offset, length;
  237.     PRArena *a, *fa;
  238.  
  239.     index = 0;
  240.     for (a = rt->gcArenaPool.first.next; a; a = a->next) {
  241.     offset = PR_UPTRDIFF(thing, a->base);
  242.     length = a->avail - a->base;
  243.     if (offset < length) {
  244.         index += offset / sizeof(JSGCThing);
  245.         for (fa = rt->gcFlagsPool.first.next; fa; fa = fa->next) {
  246.         offset = fa->avail - fa->base;
  247.         if (index < offset)
  248.             return (uint8 *)fa->base + index;
  249.         index -= offset;
  250.         }
  251.         return NULL;
  252.     }
  253.     index += length / sizeof(JSGCThing);
  254.     }
  255.     return NULL;
  256. }
  257.  
  258. JSBool
  259. js_LockGCThing(JSContext *cx, void *thing)
  260. {
  261.     uint8 *flagp, flags;
  262.  
  263.     if (!thing)
  264.     return JS_TRUE;
  265.     flagp = gc_find_flags(cx->runtime, thing);
  266.     if (!flagp)
  267.     return JS_FALSE;
  268.     flags = *flagp;
  269.     if ((flags & GCF_LOCKMASK) != GCF_LOCKMASK) {
  270.     *flagp = (uint8)(flags + GCF_LOCK);
  271.     } else {
  272.     METER(cx->runtime->gcStats.stuck++);
  273.     }
  274.     METER(cx->runtime->gcStats.lock++);
  275.     return JS_TRUE;
  276. }
  277.  
  278. JSBool
  279. js_UnlockGCThing(JSContext *cx, void *thing)
  280. {
  281.     uint8 *flagp, flags;
  282.  
  283.     if (!thing)
  284.     return JS_TRUE;
  285.     flagp = gc_find_flags(cx->runtime, thing);
  286.     if (!flagp)
  287.     return JS_FALSE;
  288.     flags = *flagp;
  289.     if ((flags & GCF_LOCKMASK) != GCF_LOCKMASK) {
  290.     *flagp = (uint8)(flags - GCF_LOCK);
  291.     } else {
  292.     METER(cx->runtime->gcStats.unstuck++);
  293.     }
  294.     METER(cx->runtime->gcStats.unlock++);
  295.     return JS_TRUE;
  296. }
  297.  
  298. #ifdef GC_MARK_DEBUG
  299.  
  300. #include <stdio.h>
  301. #include <stdlib.h>
  302. #include "prprf.h"
  303.  
  304. FILE *js_DumpGCHeap;
  305. void *js_LiveThingToFind;
  306.  
  307. typedef struct GCMarkNode GCMarkNode;
  308.  
  309. struct GCMarkNode {
  310.     void        *thing;
  311.     char        *name;
  312.     GCMarkNode  *next;
  313.     GCMarkNode  *prev;
  314. };
  315.  
  316. static void
  317. gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp)
  318. {
  319.     GCMarkNode *next = NULL;
  320.     char *path = NULL;
  321.     
  322.     while (prev) {
  323.     next = prev;
  324.     prev = prev->prev;
  325.     }
  326.     while (next) {
  327.     path = PR_sprintf_append(path, "%s.", next->name);
  328.     next = next->next;
  329.     }
  330.     if (!path)
  331.     return;
  332.  
  333.     fprintf(fp, "%08lx ", (long)thing);
  334.     switch (flags & GCF_TYPEMASK) {
  335.       case GCX_OBJECT:
  336.     fprintf(fp, "class %s", ((JSObject *)thing)->map->clasp->name);
  337.     break;
  338.       case GCX_STRING:
  339.     fprintf(fp, "bytes %s", JS_GetStringBytes((JSString *)thing));
  340.     break;
  341.       case GCX_DOUBLE:
  342.     fprintf(fp, "value %g", *(jsdouble *)thing);
  343.     break;
  344.       case GCX_DECIMAL:
  345.     break;
  346.     }
  347.     fprintf(fp, " via %s\n", path);
  348.     free(path);
  349. }
  350.  
  351. static void
  352. gc_mark_node(JSRuntime *rt, void *thing, GCMarkNode *prev);
  353.  
  354. #define GC_MARK(_rt, _thing, _name, _prev) {                                  \
  355.     GCMarkNode _node;                                                         \
  356.     _node.thing = _thing;                                                     \
  357.     _node.name  = _name;                                                      \
  358.     _node.next  = NULL;                                                       \
  359.     _node.prev  = _prev;                                                      \
  360.     if (_prev) ((GCMarkNode *)(_prev))->next = &_node;                        \
  361.     gc_mark_node(_rt, _thing, &_node);                                        \
  362. }
  363.  
  364. static void
  365. gc_mark(JSRuntime *rt, void *thing)
  366. {
  367.     GC_MARK(rt, thing, "atom", NULL);
  368. }
  369.  
  370. static void
  371. gc_mark_node(JSRuntime *rt, void *thing, GCMarkNode *prev)
  372.  
  373. #else  /* GC_MARK_DEBUG */
  374.  
  375. #define GC_MARK(rt, thing, name, prev)  gc_mark(rt, thing)
  376.  
  377. static void
  378. gc_mark(JSRuntime *rt, void *thing)
  379.  
  380. #endif /* GC_MARK_DEBUG */
  381. {
  382.     uint8 flags, *flagp;
  383.     JSObject *obj;
  384.     jsval v, *vp, *end;
  385.     JSScope *scope;
  386.  
  387.     if (!thing)
  388.     return;
  389.     flagp = gc_find_flags(rt, thing);
  390.     if (!flagp)
  391.     return;
  392.  
  393.     flags = *flagp;
  394. #ifdef GC_MARK_DEBUG
  395.     if (js_LiveThingToFind == thing)
  396.     gc_dump_thing(thing, flags, prev, stderr);
  397. #endif
  398.  
  399.     if (flags & GCF_MARK)
  400.     return;
  401.     *flagp |= GCF_MARK;
  402.     METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth)
  403.           rt->gcStats.maxdepth = rt->gcStats.depth);
  404.  
  405. #ifdef GC_MARK_DEBUG
  406.     if (js_DumpGCHeap)
  407.     gc_dump_thing(thing, flags, prev, js_DumpGCHeap);
  408. #endif
  409.  
  410.     if ((flags & GCF_TYPEMASK) == GCX_OBJECT) {
  411.     obj = thing;
  412.     vp = obj->slots;
  413.     if (vp) {
  414.         scope = (JSScope *) obj->map;
  415.         if (scope->object == obj)
  416.         end = vp + obj->map->freeslot;
  417.         else
  418.         end = vp + JS_INITIAL_NSLOTS;
  419.         for (; vp < end; vp++) {
  420.         v = *vp;
  421.         if (JSVAL_IS_GCTHING(v)) {
  422. #ifdef GC_MARK_DEBUG
  423.             uint32 slot;
  424.             JSProperty *prop;
  425.             jsval nval;
  426.             char name[32];
  427.  
  428.             slot = vp - obj->slots;
  429.             for (prop = scope->map.props; ; prop = prop->next) {
  430.             if (!prop) {
  431.                 switch (slot) {
  432.                   case JSSLOT_PROTO:
  433.                 strcpy(name, "__proto__");
  434.                 break;
  435.                   case JSSLOT_PARENT:
  436.                 strcpy(name, "__parent__");
  437.                 break;
  438.                   case JSSLOT_PRIVATE:
  439.                 strcpy(name, "__private__");
  440.                 break;
  441.                   default:
  442.                 strcpy(name, "**UNKNOWN SLOT**");
  443.                 break;
  444.                 }
  445.                 break;
  446.             }
  447.             if (prop->slot == slot) {
  448.                 nval = prop->symbols
  449.                    ? js_IdToValue(sym_id(prop->symbols))
  450.                    : prop->id;
  451.                 if (JSVAL_IS_INT(nval)) {
  452.                 PR_snprintf(name, sizeof name, "%ld",
  453.                         (long)JSVAL_TO_INT(nval));
  454.                 } else if (JSVAL_IS_STRING(nval)) {
  455.                 PR_snprintf(name, sizeof name, "%s",
  456.                         JS_GetStringBytes(JSVAL_TO_STRING(nval)));
  457.                 } else {
  458.                 strcpy(name, "**FINALIZED ATOM KEY**");
  459.                 }
  460.                 break;
  461.             }
  462.             }
  463. #endif
  464.             GC_MARK(rt, JSVAL_TO_GCTHING(v), name, prev);
  465.         }
  466.         }
  467.     }
  468.     }
  469.  
  470.     METER(rt->gcStats.depth--);
  471. }
  472.  
  473. static PRHashNumber
  474. gc_hash_root(const void *key)
  475. {
  476.     PRHashNumber num = (PRHashNumber) key;    /* help lame MSVC1.5 on Win16 */
  477.  
  478.     return num >> 2;
  479. }
  480.  
  481. PR_STATIC_CALLBACK(intN)
  482. gc_root_enumerator(PRHashEntry *he, intN i, void *arg)
  483. {
  484.     void **rp = (void **)he->key;
  485.  
  486.     if (*rp)
  487.     GC_MARK(arg, *rp, he->value ? he->value : "root", NULL);
  488.     return HT_ENUMERATE_NEXT;
  489. }
  490.  
  491. void
  492. js_ForceGC(JSContext *cx)
  493. {
  494.     PR_ASSERT(JS_IS_LOCKED(cx));
  495.     cx->newborn[GCX_OBJECT] = NULL;
  496.     cx->newborn[GCX_STRING] = NULL;
  497.     cx->newborn[GCX_DOUBLE] = NULL;
  498.     cx->runtime->gcPoke = JS_TRUE;
  499.     js_GC(cx);
  500.     PR_ArenaFinish();
  501. }
  502.  
  503. void
  504. js_GC(JSContext *cx)
  505. {
  506.     JSRuntime *rt;
  507.     JSContext *iter, *acx;
  508.     PRArena *a, *fa, **ap, **fap;
  509.     jsval v, *vp, *sp;
  510.     pruword begin, end;
  511.     JSStackFrame *fp;
  512.     uint8 flags, *flagp;
  513.     JSGCThing *thing, **flp, **oflp;
  514.     GCFinalizeOp finalizer;
  515.     JSBool a_all_clear, f_all_clear;
  516.  
  517.     rt = cx->runtime;
  518.     PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
  519.  
  520.     /* Do nothing if no assignment has executed since the last GC. */
  521.     if (!rt->gcPoke) {
  522.     METER(rt->gcStats.nopoke++);
  523.     return;
  524.     }
  525.     rt->gcPoke = JS_FALSE;
  526.     METER(rt->gcStats.poke++);
  527.  
  528.     /* Bump gcLevel and return rather than nest; the outer gc will restart. */
  529.     rt->gcLevel++;
  530.     METER(if (rt->gcLevel > rt->gcStats.maxlevel)
  531.           rt->gcStats.maxlevel = rt->gcLevel);
  532.     if (rt->gcLevel > 1)
  533.     return;
  534.  
  535.     /* Drop atoms held by the property cache, and clear property weak links. */
  536.     js_FlushPropertyCache(cx);
  537. restart:
  538.     rt->gcNumber++;
  539.  
  540.     /* Mark phase. */
  541.     PR_HashTableEnumerateEntries(rt->gcRootsHash, gc_root_enumerator, rt);
  542.     js_MarkAtomState(rt, gc_mark);
  543.     iter = NULL;
  544.     while ((acx = js_ContextIterator(rt, &iter)) != NULL) {
  545.     fp = acx->fp;
  546.     if (fp) {
  547.         sp = fp->sp;
  548.         if (sp) {
  549.         for (a = acx->stackPool.first.next; a; a = a->next) {
  550.             begin = a->base;
  551.             end = a->avail;
  552. #ifndef JS_THREADSAFE
  553.             if (PR_UPTRDIFF(sp, begin) < PR_UPTRDIFF(end, begin))
  554.             end = (pruword)sp;
  555. #endif
  556.             for (vp = (jsval *)begin; vp < (jsval *)end; vp++) {
  557.             v = *vp;
  558.             if (JSVAL_IS_GCTHING(v))
  559.                 GC_MARK(rt, JSVAL_TO_GCTHING(v), "stack", NULL);
  560.             }
  561.         }
  562.         }
  563.         do {
  564.         GC_MARK(rt, fp->scopeChain, "scope chain", NULL);
  565.         GC_MARK(rt, fp->thisp, "this", NULL);
  566.         if (JSVAL_IS_GCTHING(fp->rval))
  567.             GC_MARK(rt, JSVAL_TO_GCTHING(fp->rval), "rval", NULL);
  568.         if (fp->object)
  569.             GC_MARK(rt, fp->object, "call object", NULL);
  570. #if JS_HAS_SHARP_VARS
  571.         if (fp->sharpArray)
  572.             GC_MARK(rt, fp->sharpArray, "sharp array", NULL);
  573. #endif
  574.         } while ((fp = fp->down) != NULL);
  575.     }
  576.     GC_MARK(rt, acx->globalObject, "global object", NULL);
  577.     GC_MARK(rt, acx->newborn[GCX_OBJECT], "newborn object", NULL);
  578.     GC_MARK(rt, acx->newborn[GCX_STRING], "newborn string", NULL);
  579.     GC_MARK(rt, acx->newborn[GCX_DOUBLE], "newborn double", NULL);
  580.     }
  581.  
  582.     /* Sweep phase. */
  583.     fa = rt->gcFlagsPool.first.next;
  584.     flagp = (uint8 *)fa->base;
  585.     for (a = rt->gcArenaPool.first.next; a; a = a->next) {
  586.     for (thing = (JSGCThing *)a->base; thing < (JSGCThing *)a->avail;
  587.          thing++) {
  588.         if (flagp >= (uint8 *)fa->avail) {
  589.         fa = fa->next;
  590.         PR_ASSERT(fa);
  591.         if (!fa) {
  592.             METER(rt->gcStats.badflag++);
  593.             goto out;
  594.         }
  595.         flagp = (uint8 *)fa->base;
  596.         }
  597.         flags = *flagp;
  598.         if (flags & GCF_MARK) {
  599.         *flagp &= ~GCF_MARK;
  600.         } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) {
  601.         finalizer = gc_finalizers[flags & GCF_TYPEMASK];
  602.         if (finalizer) {
  603.             *flagp |= GCF_FINAL;
  604.             finalizer(cx, thing);
  605.         }
  606.  
  607.         /*
  608.          * Set flags to GCF_FINAL, signifying that thing is free,
  609.          * but don't thread thing onto rt->gcFreeList.  We rebuild
  610.          * the freelist below while looking for free-able arenas.
  611.          */
  612.         *flagp = GCF_FINAL;
  613.         PR_ASSERT(rt->gcBytes >= sizeof(JSGCThing)+sizeof(uint8));
  614.         rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8);
  615.         }
  616.         flagp++;
  617.     }
  618.     }
  619.  
  620.     /* Free unused arenas and rebuild the freelist. */
  621.     ap = &rt->gcArenaPool.first.next;
  622.     a = *ap;
  623.     if (!a)
  624.     goto out;
  625.     thing = (JSGCThing *)a->base;
  626.     a_all_clear = f_all_clear = JS_TRUE;
  627.     flp = oflp = &rt->gcFreeList;
  628.     *flp = NULL;
  629.     METER(rt->gcStats.freelen = 0);
  630.  
  631.     fap = &rt->gcFlagsPool.first.next;
  632.     while ((fa = *fap) != NULL) {
  633.     /* XXX optimize by unrolling to use word loads */
  634.     for (flagp = (uint8 *)fa->base; ; flagp++) {
  635.         PR_ASSERT(a);
  636.         if (!a) {
  637.         METER(rt->gcStats.badarena++);
  638.         goto out;
  639.         }
  640.         if (thing >= (JSGCThing *)a->avail) {
  641.         if (a_all_clear) {
  642.             PR_ARENA_DESTROY(&rt->gcArenaPool, a, ap);
  643.             flp = oflp;
  644.             METER(rt->gcStats.afree++);
  645.         } else {
  646.             ap = &a->next;
  647.             a_all_clear = JS_TRUE;
  648.             oflp = flp;
  649.         }
  650.         a = *ap;
  651.         if (!a)
  652.             break;
  653.         thing = (JSGCThing *)a->base;
  654.         }
  655.         if (flagp >= (uint8 *)fa->avail)
  656.         break;
  657.         if (*flagp != GCF_FINAL) {
  658.         a_all_clear = f_all_clear = JS_FALSE;
  659.         } else {
  660.         thing->flagp = flagp;
  661.         *flp = thing;
  662.         flp = &thing->next;
  663.         METER(rt->gcStats.freelen++);
  664.         }
  665.         thing++;
  666.     }
  667.     if (f_all_clear) {
  668.         PR_ARENA_DESTROY(&rt->gcFlagsPool, fa, fap);
  669.         METER(rt->gcStats.fafree++);
  670.     } else {
  671.         fap = &fa->next;
  672.         f_all_clear = JS_TRUE;
  673.     }
  674.     }
  675.  
  676.     /* Terminate the new freelist. */
  677.     *flp = NULL;
  678.  
  679. out:
  680.     if (rt->gcLevel > 1) {
  681.     rt->gcLevel = 1;
  682.     goto restart;
  683.     }
  684.     rt->gcLevel = 0;
  685.     rt->gcLastBytes = rt->gcBytes;
  686. }
  687.