2009-06-10 18:29:44 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2007-03-22 10:30:00 -07:00
|
|
|
* vim: set ts=8 sw=4 et tw=78:
|
|
|
|
*
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
|
|
* March 31, 1998.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* JS Mark-and-Sweep Garbage Collector.
|
|
|
|
*
|
|
|
|
* This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see
|
|
|
|
* jsgc.h). It allocates from a special GC arena pool with each arena allocated
|
|
|
|
* using malloc. It uses an ideally parallel array of flag bytes to hold the
|
|
|
|
* mark bit, finalizer type index, etc.
|
|
|
|
*
|
|
|
|
* XXX swizzle page to freelist for better locality of reference
|
|
|
|
*/
|
|
|
|
#include <stdlib.h> /* for free */
|
2008-05-28 19:07:32 -07:00
|
|
|
#include <math.h>
|
2007-03-22 10:30:00 -07:00
|
|
|
#include <string.h> /* for memset used when DEBUG */
|
|
|
|
#include "jstypes.h"
|
2009-03-18 11:38:16 -07:00
|
|
|
#include "jsstdint.h"
|
2010-10-01 16:46:54 -07:00
|
|
|
#include "jsutil.h"
|
|
|
|
#include "jshash.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsbit.h"
|
|
|
|
#include "jsclist.h"
|
2008-05-28 19:07:32 -07:00
|
|
|
#include "jsprf.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
#include "jsatom.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jscntxt.h"
|
2008-09-05 10:19:17 -07:00
|
|
|
#include "jsversion.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsdbgapi.h"
|
|
|
|
#include "jsexn.h"
|
|
|
|
#include "jsfun.h"
|
|
|
|
#include "jsgc.h"
|
2010-04-12 13:59:19 -07:00
|
|
|
#include "jsgcchunk.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsinterp.h"
|
|
|
|
#include "jsiter.h"
|
|
|
|
#include "jslock.h"
|
|
|
|
#include "jsnum.h"
|
|
|
|
#include "jsobj.h"
|
2007-07-08 02:03:34 -07:00
|
|
|
#include "jsparse.h"
|
2010-05-18 19:21:43 -07:00
|
|
|
#include "jsproxy.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsscope.h"
|
|
|
|
#include "jsscript.h"
|
2009-01-30 15:40:05 -08:00
|
|
|
#include "jsstaticcheck.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsstr.h"
|
2010-09-02 20:04:33 -07:00
|
|
|
#include "methodjit/MethodJIT.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
#include "jsxml.h"
|
|
|
|
#endif
|
|
|
|
|
2010-09-01 14:09:54 -07:00
|
|
|
#include "jsprobes.h"
|
2010-03-28 13:34:16 -07:00
|
|
|
#include "jscntxtinlines.h"
|
2010-08-09 22:43:33 -07:00
|
|
|
#include "jsinterpinlines.h"
|
2009-12-30 03:06:26 -08:00
|
|
|
#include "jsobjinlines.h"
|
2010-05-20 13:50:08 -07:00
|
|
|
#include "jshashtable.h"
|
2009-12-30 03:06:26 -08:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
#include "jsstrinlines.h"
|
|
|
|
#include "jscompartment.h"
|
|
|
|
|
2010-06-21 05:22:32 -07:00
|
|
|
#ifdef MOZ_VALGRIND
|
|
|
|
# define JS_VALGRIND
|
|
|
|
#endif
|
|
|
|
#ifdef JS_VALGRIND
|
|
|
|
# include <valgrind/memcheck.h>
|
|
|
|
#endif
|
|
|
|
|
2010-01-22 14:49:18 -08:00
|
|
|
using namespace js;
|
2010-09-24 10:54:39 -07:00
|
|
|
using namespace js::gc;
|
2010-01-22 14:49:18 -08:00
|
|
|
|
2009-03-05 03:12:50 -08:00
|
|
|
/*
|
2010-07-14 23:19:36 -07:00
|
|
|
* Check that JSTRACE_XML follows JSTRACE_OBJECT and JSTRACE_STRING.
|
2009-03-05 03:12:50 -08:00
|
|
|
*/
|
|
|
|
JS_STATIC_ASSERT(JSTRACE_OBJECT == 0);
|
2010-07-14 23:19:36 -07:00
|
|
|
JS_STATIC_ASSERT(JSTRACE_STRING == 1);
|
|
|
|
JS_STATIC_ASSERT(JSTRACE_XML == 2);
|
2009-03-05 03:12:50 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* JS_IS_VALID_TRACE_KIND assumes that JSTRACE_STRING is the last non-xml
|
|
|
|
* trace kind when JS_HAS_XML_SUPPORT is false.
|
|
|
|
*/
|
|
|
|
JS_STATIC_ASSERT(JSTRACE_STRING + 1 == JSTRACE_XML);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
2010-09-24 10:54:39 -07:00
|
|
|
* Everything we store in the heap must be a multiple of the cell size.
|
2007-09-16 06:03:17 -07:00
|
|
|
*/
|
2010-11-15 12:39:00 -08:00
|
|
|
JS_STATIC_ASSERT(sizeof(JSString) % sizeof(FreeCell) == 0);
|
|
|
|
JS_STATIC_ASSERT(sizeof(JSShortString) % sizeof(FreeCell) == 0);
|
|
|
|
JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(FreeCell) == 0);
|
|
|
|
JS_STATIC_ASSERT(sizeof(JSFunction) % sizeof(FreeCell) == 0);
|
2010-04-12 13:59:19 -07:00
|
|
|
#ifdef JSXML
|
2010-11-15 12:39:00 -08:00
|
|
|
JS_STATIC_ASSERT(sizeof(JSXML) % sizeof(FreeCell) == 0);
|
2010-04-12 13:59:19 -07:00
|
|
|
#endif
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/*
|
|
|
|
* All arenas must be exactly 4k.
|
|
|
|
*/
|
2010-11-15 12:39:00 -08:00
|
|
|
JS_STATIC_ASSERT(sizeof(Arena<JSString>) == 4096);
|
|
|
|
JS_STATIC_ASSERT(sizeof(Arena<JSExternalString>) == 4096);
|
|
|
|
JS_STATIC_ASSERT(sizeof(Arena<JSShortString>) == 4096);
|
|
|
|
JS_STATIC_ASSERT(sizeof(Arena<JSObject>) == 4096);
|
|
|
|
JS_STATIC_ASSERT(sizeof(Arena<JSFunction>) == 4096);
|
|
|
|
JS_STATIC_ASSERT(sizeof(Arena<JSXML>) == 4096);
|
2010-09-24 10:54:39 -07:00
|
|
|
|
2010-08-24 18:57:14 -07:00
|
|
|
#ifdef JS_GCMETER
|
|
|
|
# define METER(x) ((void) (x))
|
|
|
|
# define METER_IF(condition, x) ((void) ((condition) && (x)))
|
|
|
|
#else
|
|
|
|
# define METER(x) ((void) 0)
|
|
|
|
# define METER_IF(condition, x) ((void) 0)
|
|
|
|
#endif
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
# define METER_UPDATE_MAX(maxLval, rval) \
|
|
|
|
METER_IF((maxLval) < (rval), (maxLval) = (rval))
|
2010-01-14 00:27:32 -08:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
namespace js{
|
|
|
|
namespace gc{
|
2010-01-14 00:27:32 -08:00
|
|
|
|
2010-10-13 11:49:22 -07:00
|
|
|
/* This array should be const, but that doesn't link right under GCC. */
|
|
|
|
FinalizeKind slotsToThingKind[] = {
|
|
|
|
/* 0 */ FINALIZE_OBJECT0, FINALIZE_OBJECT2, FINALIZE_OBJECT2, FINALIZE_OBJECT4,
|
|
|
|
/* 4 */ FINALIZE_OBJECT4, FINALIZE_OBJECT8, FINALIZE_OBJECT8, FINALIZE_OBJECT8,
|
|
|
|
/* 8 */ FINALIZE_OBJECT8, FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
|
|
|
|
/* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16,
|
|
|
|
/* 16 */ FINALIZE_OBJECT16
|
|
|
|
};
|
|
|
|
|
|
|
|
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT);
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/* Initialize the arena and setup the free list. */
|
|
|
|
template <typename T>
|
|
|
|
void
|
|
|
|
Arena<T>::init(JSCompartment *compartment, unsigned thingKind)
|
|
|
|
{
|
|
|
|
aheader.compartment = compartment;
|
|
|
|
aheader.thingKind = thingKind;
|
|
|
|
aheader.freeList = &t.things[0].cell;
|
|
|
|
JS_ASSERT(sizeof(T) == sizeof(ThingOrCell<T>));
|
|
|
|
ThingOrCell<T> *thing = &t.things[0];
|
|
|
|
ThingOrCell<T> *last = &t.things[JS_ARRAY_LENGTH(t.things) - 1];
|
|
|
|
while (thing < last) {
|
|
|
|
thing->cell.link = &(thing + 1)->cell;
|
|
|
|
++thing;
|
|
|
|
}
|
|
|
|
last->cell.link = NULL;
|
|
|
|
#ifdef DEBUG
|
2011-03-10 15:27:21 -08:00
|
|
|
aheader.thingSize = sizeof(T);
|
|
|
|
aheader.isUsed = true;
|
2010-09-24 10:54:39 -07:00
|
|
|
aheader.hasFreeThings = true;
|
|
|
|
#endif
|
|
|
|
}
|
2007-09-15 10:19:32 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template <typename T>
|
|
|
|
bool
|
|
|
|
Arena<T>::inFreeList(void *thing) const
|
|
|
|
{
|
|
|
|
FreeCell *cursor = aheader.freeList;
|
|
|
|
while (cursor) {
|
|
|
|
JS_ASSERT(aheader.thingSize == sizeof(T));
|
|
|
|
JS_ASSERT(!cursor->isMarked());
|
2010-04-12 10:15:30 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/* If the cursor moves past the thing, it's not in the freelist. */
|
|
|
|
if (thing < cursor)
|
|
|
|
break;
|
2010-04-12 10:15:30 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/* If we find it on the freelist, it's dead. */
|
|
|
|
if (thing == cursor)
|
|
|
|
return true;
|
|
|
|
JS_ASSERT_IF(cursor->link, cursor < cursor->link);
|
|
|
|
cursor = cursor->link;
|
2010-01-14 00:27:32 -08:00
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
return false;
|
|
|
|
}
|
2010-01-14 00:27:32 -08:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template<typename T>
|
|
|
|
inline ConservativeGCTest
|
|
|
|
Arena<T>::mark(T *thing, JSTracer *trc)
|
|
|
|
{
|
2010-11-17 12:39:45 -08:00
|
|
|
T *alignedThing = getAlignedThing(thing);
|
2010-04-12 10:15:30 -07:00
|
|
|
|
2010-11-17 12:39:45 -08:00
|
|
|
if (alignedThing > &t.things[ThingsPerArena-1].t || alignedThing < &t.things[0].t)
|
2010-09-24 10:54:39 -07:00
|
|
|
return CGCT_NOTARENA;
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2011-03-10 15:27:21 -08:00
|
|
|
if (!aheader.compartment || inFreeList(alignedThing))
|
2010-09-24 10:54:39 -07:00
|
|
|
return CGCT_NOTLIVE;
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2011-03-10 15:27:21 -08:00
|
|
|
JS_ASSERT(sizeof(T) == aheader.thingSize);
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_SET_TRACING_NAME(trc, "machine stack");
|
2011-03-22 14:19:09 -07:00
|
|
|
js::gc::Mark(trc, alignedThing);
|
2010-04-12 10:15:30 -07:00
|
|
|
|
2010-11-17 12:39:45 -08:00
|
|
|
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
|
|
|
|
if (alignedThing != thing)
|
|
|
|
return CGCT_VALIDWITHOFFSET;
|
|
|
|
#endif
|
2010-09-24 10:54:39 -07:00
|
|
|
return CGCT_VALID;
|
2010-04-12 13:59:19 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
bool
|
|
|
|
checkArenaListsForThing(JSCompartment *comp, void *thing) {
|
2010-10-13 11:49:22 -07:00
|
|
|
if (comp->arenas[FINALIZE_OBJECT0].arenasContainThing<JSObject>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_OBJECT2].arenasContainThing<JSObject_Slots2>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_OBJECT4].arenasContainThing<JSObject_Slots4>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_OBJECT8].arenasContainThing<JSObject_Slots8>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_OBJECT12].arenasContainThing<JSObject_Slots12>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_OBJECT16].arenasContainThing<JSObject_Slots16>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_FUNCTION].arenasContainThing<JSFunction>(thing) ||
|
2010-09-24 10:54:39 -07:00
|
|
|
#if JS_HAS_XML_SUPPORT
|
2010-10-13 11:49:22 -07:00
|
|
|
comp->arenas[FINALIZE_XML].arenasContainThing<JSXML>(thing) ||
|
2010-09-24 10:54:39 -07:00
|
|
|
#endif
|
2010-11-15 12:39:00 -08:00
|
|
|
comp->arenas[FINALIZE_STRING].arenasContainThing<JSString>(thing) ||
|
|
|
|
comp->arenas[FINALIZE_EXTERNAL_STRING].arenasContainThing<JSExternalString>(thing) ||
|
2010-10-13 11:49:22 -07:00
|
|
|
comp->arenas[FINALIZE_SHORT_STRING].arenasContainThing<JSShortString>(thing)) {
|
2010-09-24 10:54:39 -07:00
|
|
|
return true;
|
|
|
|
}
|
2010-11-15 12:39:00 -08:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
return false;
|
2010-05-18 03:01:33 -07:00
|
|
|
}
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
bool
|
|
|
|
checkArenaListAllUnmarked(JSCompartment *comp) {
|
|
|
|
for (unsigned i = 0; i < FINALIZE_LIMIT; i++) {
|
|
|
|
if (comp->arenas[i].markedThingsInArenaList())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
#endif
|
2010-05-18 03:01:33 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
} /* namespace gc */
|
|
|
|
} /* namespace js */
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
void
|
|
|
|
JSCompartment::finishArenaLists()
|
2010-01-14 00:27:32 -08:00
|
|
|
{
|
2010-10-13 11:49:22 -07:00
|
|
|
for (int i = 0; i < FINALIZE_LIMIT; i++)
|
|
|
|
arenas[i].releaseAll();
|
2008-02-26 13:01:42 -08:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
void
|
|
|
|
Chunk::clearMarkBitmap()
|
2010-07-15 17:58:36 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
PodZero(&bitmaps[0], ArenasPerChunk);
|
2010-07-15 17:58:36 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
void
|
|
|
|
Chunk::init(JSRuntime *rt)
|
2010-04-12 10:15:30 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
info.runtime = rt;
|
|
|
|
info.age = 0;
|
|
|
|
info.emptyArenaLists.init();
|
|
|
|
info.emptyArenaLists.cellFreeList = &arenas[0];
|
|
|
|
Arena<FreeCell> *arena = &arenas[0];
|
|
|
|
Arena<FreeCell> *last = &arenas[JS_ARRAY_LENGTH(arenas) - 1];
|
|
|
|
while (arena < last) {
|
|
|
|
arena->header()->next = arena + 1;
|
2011-03-10 15:27:21 -08:00
|
|
|
arena->header()->compartment = NULL;
|
|
|
|
#ifdef DEBUG
|
2010-09-24 15:07:02 -07:00
|
|
|
arena->header()->isUsed = false;
|
2011-03-10 15:27:21 -08:00
|
|
|
#endif
|
2010-09-24 10:54:39 -07:00
|
|
|
++arena;
|
|
|
|
}
|
|
|
|
last->header()->next = NULL;
|
2011-03-10 15:27:21 -08:00
|
|
|
last->header()->compartment = NULL;
|
|
|
|
#ifdef DEBUG
|
2010-09-24 15:07:02 -07:00
|
|
|
last->header()->isUsed = false;
|
2011-03-10 15:27:21 -08:00
|
|
|
#endif
|
2010-09-24 10:54:39 -07:00
|
|
|
info.numFree = ArenasPerChunk;
|
2010-04-12 10:15:30 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
bool
|
|
|
|
Chunk::unused()
|
2010-04-12 10:15:30 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
return info.numFree == ArenasPerChunk;
|
|
|
|
}
|
2010-07-15 17:58:36 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
bool
|
|
|
|
Chunk::hasAvailableArenas()
|
|
|
|
{
|
|
|
|
return info.numFree > 0;
|
2010-04-12 10:15:30 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
bool
|
|
|
|
Chunk::withinArenasRange(Cell *cell)
|
2010-04-12 13:59:19 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
uintptr_t addr = uintptr_t(cell);
|
|
|
|
if (addr >= uintptr_t(&arenas[0]) && addr < uintptr_t(&arenas[ArenasPerChunk]))
|
|
|
|
return true;
|
|
|
|
return false;
|
2010-04-12 13:59:19 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template <typename T>
|
|
|
|
Arena<T> *
|
|
|
|
Chunk::allocateArena(JSCompartment *comp, unsigned thingKind)
|
2010-04-12 13:59:19 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(hasAvailableArenas());
|
|
|
|
Arena<T> *arena = info.emptyArenaLists.getNext<T>(comp, thingKind);
|
|
|
|
JS_ASSERT(arena);
|
|
|
|
JS_ASSERT(arena->header()->isUsed);
|
|
|
|
--info.numFree;
|
2010-10-07 13:43:52 -07:00
|
|
|
|
|
|
|
JSRuntime *rt = info.runtime;
|
2010-09-24 10:54:39 -07:00
|
|
|
rt->gcBytes += sizeof(Arena<T>);
|
2011-01-07 23:44:57 -08:00
|
|
|
comp->gcBytes += sizeof(Arena<T>);
|
|
|
|
if (comp->gcBytes >= comp->gcTriggerBytes)
|
|
|
|
TriggerCompartmentGC(comp);
|
2010-09-24 10:54:39 -07:00
|
|
|
METER(rt->gcStats.nallarenas++);
|
|
|
|
return arena;
|
2010-04-12 13:59:19 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template <typename T>
|
|
|
|
void
|
|
|
|
Chunk::releaseArena(Arena<T> *arena)
|
2010-04-12 13:59:19 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
JSRuntime *rt = info.runtime;
|
2011-01-07 23:44:57 -08:00
|
|
|
JSCompartment *comp = arena->header()->compartment;
|
2010-09-24 10:54:39 -07:00
|
|
|
METER(rt->gcStats.afree++);
|
|
|
|
JS_ASSERT(rt->gcStats.nallarenas != 0);
|
|
|
|
METER(rt->gcStats.nallarenas--);
|
|
|
|
JS_ASSERT(rt->gcBytes >= sizeof(Arena<T>));
|
2011-01-07 23:44:57 -08:00
|
|
|
JS_ASSERT(comp->gcBytes >= sizeof(Arena<T>));
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
rt->gcBytes -= sizeof(Arena<T>);
|
2011-01-07 23:44:57 -08:00
|
|
|
comp->gcBytes -= sizeof(Arena<T>);
|
2010-10-13 11:49:22 -07:00
|
|
|
info.emptyArenaLists.insert((Arena<Cell> *)arena);
|
2011-03-10 15:27:21 -08:00
|
|
|
#ifdef DEBUG
|
2010-09-24 10:54:39 -07:00
|
|
|
arena->header()->isUsed = false;
|
2011-03-10 15:27:21 -08:00
|
|
|
#endif
|
|
|
|
arena->header()->compartment = NULL;
|
2010-09-24 10:54:39 -07:00
|
|
|
++info.numFree;
|
|
|
|
if (unused())
|
|
|
|
info.age = 0;
|
2010-04-12 13:59:19 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
JSRuntime *
|
|
|
|
Chunk::getRuntime()
|
|
|
|
{
|
|
|
|
return info.runtime;
|
2010-01-14 00:27:32 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
inline jsuword
|
2010-04-12 13:59:19 -07:00
|
|
|
GetGCChunk(JSRuntime *rt)
|
2009-09-17 15:40:37 -07:00
|
|
|
{
|
2010-06-07 02:17:15 -07:00
|
|
|
void *p = rt->gcChunkAllocator->alloc();
|
2010-03-25 16:11:27 -07:00
|
|
|
#ifdef MOZ_GCTIMER
|
2010-04-12 13:59:19 -07:00
|
|
|
if (p)
|
|
|
|
JS_ATOMIC_INCREMENT(&newChunkCount);
|
2009-09-17 15:40:37 -07:00
|
|
|
#endif
|
2010-04-12 13:59:19 -07:00
|
|
|
METER_IF(p, rt->gcStats.nchunks++);
|
|
|
|
METER_UPDATE_MAX(rt->gcStats.maxnchunks, rt->gcStats.nchunks);
|
2010-08-05 05:16:56 -07:00
|
|
|
return reinterpret_cast<jsuword>(p);
|
2009-09-17 15:40:37 -07:00
|
|
|
}
|
|
|
|
|
2010-04-12 13:59:19 -07:00
|
|
|
inline void
|
2010-04-22 23:58:44 -07:00
|
|
|
ReleaseGCChunk(JSRuntime *rt, jsuword chunk)
|
2009-09-17 15:40:37 -07:00
|
|
|
{
|
2010-04-22 23:58:44 -07:00
|
|
|
void *p = reinterpret_cast<void *>(chunk);
|
2010-04-12 13:59:19 -07:00
|
|
|
JS_ASSERT(p);
|
2010-03-25 16:11:27 -07:00
|
|
|
#ifdef MOZ_GCTIMER
|
|
|
|
JS_ATOMIC_INCREMENT(&destroyChunkCount);
|
|
|
|
#endif
|
2010-04-12 13:59:19 -07:00
|
|
|
JS_ASSERT(rt->gcStats.nchunks != 0);
|
|
|
|
METER(rt->gcStats.nchunks--);
|
2010-06-07 02:17:15 -07:00
|
|
|
rt->gcChunkAllocator->free(p);
|
2009-09-17 15:40:37 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
inline Chunk *
|
|
|
|
AllocateGCChunk(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
Chunk *p = (Chunk *)rt->gcChunkAllocator->alloc();
|
|
|
|
#ifdef MOZ_GCTIMER
|
|
|
|
if (p)
|
|
|
|
JS_ATOMIC_INCREMENT(&newChunkCount);
|
|
|
|
#endif
|
|
|
|
METER_IF(p, rt->gcStats.nchunks++);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
ReleaseGCChunk(JSRuntime *rt, Chunk *p)
|
|
|
|
{
|
|
|
|
JS_ASSERT(p);
|
|
|
|
#ifdef MOZ_GCTIMER
|
|
|
|
JS_ATOMIC_INCREMENT(&destroyChunkCount);
|
|
|
|
#endif
|
|
|
|
JS_ASSERT(rt->gcStats.nchunks != 0);
|
|
|
|
METER(rt->gcStats.nchunks--);
|
|
|
|
rt->gcChunkAllocator->free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Chunk *
|
2010-10-07 13:43:52 -07:00
|
|
|
PickChunk(JSRuntime *rt)
|
2007-09-16 06:03:17 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
Chunk *chunk;
|
|
|
|
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) {
|
|
|
|
if (r.front()->hasAvailableArenas())
|
|
|
|
return r.front();
|
2010-04-12 13:59:19 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
chunk = AllocateGCChunk(rt);
|
|
|
|
if (!chunk)
|
|
|
|
return NULL;
|
2010-08-05 05:16:56 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/*
|
2010-09-24 15:07:02 -07:00
|
|
|
* FIXME bug 583732 - chunk is newly allocated and cannot be present in
|
2010-09-24 10:54:39 -07:00
|
|
|
* the table so using ordinary lookupForAdd is suboptimal here.
|
|
|
|
*/
|
|
|
|
GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk);
|
|
|
|
JS_ASSERT(!p);
|
|
|
|
if (!rt->gcChunkSet.add(p, chunk)) {
|
|
|
|
ReleaseGCChunk(rt, chunk);
|
|
|
|
return NULL;
|
2010-08-05 05:16:56 -07:00
|
|
|
}
|
2009-09-17 15:40:37 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
chunk->init(rt);
|
2010-04-12 10:15:30 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
return chunk;
|
2010-04-22 23:58:44 -07:00
|
|
|
}
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2010-04-22 23:58:44 -07:00
|
|
|
static void
|
2010-09-24 10:54:39 -07:00
|
|
|
ExpireGCChunks(JSRuntime *rt)
|
2010-04-22 23:58:44 -07:00
|
|
|
{
|
2011-02-19 22:59:49 -08:00
|
|
|
static const size_t MaxAge = 3;
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/* Remove unused chunks. */
|
|
|
|
AutoLockGC lock(rt);
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2011-02-19 22:59:49 -08:00
|
|
|
rt->gcChunksWaitingToExpire = 0;
|
2010-08-05 05:16:56 -07:00
|
|
|
for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) {
|
2010-09-24 10:54:39 -07:00
|
|
|
Chunk *chunk = e.front();
|
|
|
|
JS_ASSERT(chunk->info.runtime == rt);
|
2011-02-19 22:59:49 -08:00
|
|
|
if (chunk->unused()) {
|
|
|
|
if (chunk->info.age++ > MaxAge) {
|
|
|
|
e.removeFront();
|
|
|
|
ReleaseGCChunk(rt, chunk);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rt->gcChunksWaitingToExpire++;
|
2010-04-12 13:59:19 -07:00
|
|
|
}
|
|
|
|
}
|
2010-04-22 23:58:44 -07:00
|
|
|
}
|
2009-09-17 15:40:37 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template <typename T>
|
|
|
|
static Arena<T> *
|
|
|
|
AllocateArena(JSContext *cx, unsigned thingKind)
|
2009-10-02 07:34:22 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
JSRuntime *rt = cx->runtime;
|
2010-10-07 13:43:52 -07:00
|
|
|
AutoLockGC lock(rt);
|
|
|
|
Chunk *chunk = cx->compartment->chunk;
|
|
|
|
if (!chunk || !chunk->hasAvailableArenas()) {
|
|
|
|
chunk = PickChunk(rt);
|
|
|
|
if (!chunk) {
|
|
|
|
TriggerGC(rt);
|
|
|
|
return NULL;
|
2010-09-24 10:54:39 -07:00
|
|
|
}
|
2010-10-07 13:43:52 -07:00
|
|
|
cx->compartment->chunk = chunk;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2010-10-07 13:43:52 -07:00
|
|
|
return chunk->allocateArena<T>(cx->compartment, thingKind);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-07-15 17:58:36 -07:00
|
|
|
JS_FRIEND_API(bool)
|
2011-03-23 11:57:15 -07:00
|
|
|
IsAboutToBeFinalized(JSContext *cx, const void *thing)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-03-14 13:59:53 -07:00
|
|
|
if (JSAtom::isStatic(thing))
|
2009-12-22 12:50:44 -08:00
|
|
|
return false;
|
2011-01-07 23:44:57 -08:00
|
|
|
JS_ASSERT(cx);
|
|
|
|
|
2011-03-23 11:57:15 -07:00
|
|
|
JSCompartment *thingCompartment = reinterpret_cast<const Cell *>(thing)->compartment();
|
2011-01-07 23:44:57 -08:00
|
|
|
JSRuntime *rt = cx->runtime;
|
2011-01-08 20:06:29 -08:00
|
|
|
JS_ASSERT(rt == thingCompartment->rt);
|
2011-01-07 23:44:57 -08:00
|
|
|
if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != thingCompartment)
|
|
|
|
return false;
|
2009-12-22 12:50:44 -08:00
|
|
|
|
2011-03-23 11:57:15 -07:00
|
|
|
return !reinterpret_cast<const Cell *>(thing)->isMarked();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-07-15 17:58:36 -07:00
|
|
|
JS_FRIEND_API(bool)
|
2011-02-16 12:47:08 -08:00
|
|
|
js_GCThingIsMarked(void *thing, uintN color = BLACK)
|
2010-07-15 17:58:36 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(thing);
|
|
|
|
AssertValidColor(thing, color);
|
|
|
|
return reinterpret_cast<Cell *>(thing)->isMarked(color);
|
2010-07-15 17:58:36 -07:00
|
|
|
}
|
|
|
|
|
2010-12-17 16:33:04 -08:00
|
|
|
/*
|
|
|
|
* 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all
|
|
|
|
* JIT code is discarded in inactive compartments, regardless of how often that
|
|
|
|
* code runs.
|
|
|
|
*/
|
|
|
|
static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSBool
|
2009-09-17 15:40:37 -07:00
|
|
|
js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
|
|
|
{
|
2010-08-05 05:16:56 -07:00
|
|
|
/*
|
|
|
|
* Make room for at least 16 chunks so the table would not grow before
|
|
|
|
* the browser starts up.
|
|
|
|
*/
|
|
|
|
if (!rt->gcChunkSet.init(16))
|
|
|
|
return false;
|
|
|
|
|
2010-05-20 13:50:08 -07:00
|
|
|
if (!rt->gcRootsHash.init(256))
|
2009-12-24 01:31:07 -08:00
|
|
|
return false;
|
2010-06-22 02:19:04 -07:00
|
|
|
|
2010-05-20 13:50:08 -07:00
|
|
|
if (!rt->gcLocksHash.init(256))
|
2009-12-24 01:31:07 -08:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-04-27 12:40:28 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-09-07 14:08:20 -07:00
|
|
|
rt->gcLock = JS_NEW_LOCK();
|
|
|
|
if (!rt->gcLock)
|
|
|
|
return false;
|
|
|
|
rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
|
|
|
|
if (!rt->gcDone)
|
|
|
|
return false;
|
|
|
|
rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
|
|
|
|
if (!rt->requestDone)
|
|
|
|
return false;
|
|
|
|
if (!rt->gcHelperThread.init(rt))
|
2010-04-27 06:46:24 -07:00
|
|
|
return false;
|
2010-04-27 12:40:28 -07:00
|
|
|
#endif
|
2010-04-27 06:46:24 -07:00
|
|
|
|
2009-08-25 14:42:42 -07:00
|
|
|
/*
|
|
|
|
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
|
|
|
|
* for default backward API compatibility.
|
|
|
|
*/
|
2009-10-18 08:40:19 -07:00
|
|
|
rt->gcMaxBytes = maxbytes;
|
|
|
|
rt->setGCMaxMallocBytes(maxbytes);
|
|
|
|
|
2008-09-12 15:11:48 -07:00
|
|
|
rt->gcEmptyArenaPoolLifespan = 30000;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-10-07 13:43:52 -07:00
|
|
|
rt->gcTriggerFactor = uint32(100.0f * GC_HEAP_GROWTH_FACTOR);
|
2009-08-25 14:42:42 -07:00
|
|
|
|
2011-01-13 14:42:36 -08:00
|
|
|
rt->atomsCompartment->setGCLastBytes(8192);
|
2011-01-07 23:44:57 -08:00
|
|
|
|
2009-08-25 14:42:42 -07:00
|
|
|
/*
|
|
|
|
* The assigned value prevents GC from running when GC memory is too low
|
|
|
|
* (during JS engine start).
|
|
|
|
*/
|
|
|
|
rt->setGCLastBytes(8192);
|
2009-01-27 09:21:51 -08:00
|
|
|
|
2010-12-17 16:33:04 -08:00
|
|
|
rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME;
|
|
|
|
|
2010-03-10 15:34:12 -08:00
|
|
|
METER(PodZero(&rt->gcStats));
|
2009-12-24 01:31:07 -08:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-06-04 07:22:28 -07:00
|
|
|
namespace js {
|
|
|
|
|
2010-10-13 11:49:22 -07:00
|
|
|
template <typename T>
|
|
|
|
static inline ConservativeGCTest
|
|
|
|
MarkCell(Cell *cell, JSTracer *trc)
|
|
|
|
{
|
|
|
|
return GetArena<T>(cell)->mark((T *)cell, trc);
|
|
|
|
}
|
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
/*
|
2010-11-17 12:39:45 -08:00
|
|
|
* Returns CGCT_VALID or CGCT_VALIDWITHOFFSET and mark it if the w can be a
|
|
|
|
* live GC thing and sets thingKind accordingly. Otherwise returns the
|
|
|
|
* reason for rejection.
|
2010-08-05 05:16:56 -07:00
|
|
|
*/
|
|
|
|
inline ConservativeGCTest
|
2010-11-17 12:39:45 -08:00
|
|
|
MarkIfGCThingWord(JSTracer *trc, jsuword w, uint32 &thingKind)
|
2010-06-04 07:22:28 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
JSRuntime *rt = trc->context->runtime;
|
2010-06-21 05:22:32 -07:00
|
|
|
/*
|
|
|
|
* The conservative scanner may access words that valgrind considers as
|
|
|
|
* undefined. To avoid false positives and not to alter valgrind view of
|
|
|
|
* the memory we make as memcheck-defined the argument, a copy of the
|
|
|
|
* original word. See bug 572678.
|
|
|
|
*/
|
|
|
|
#ifdef JS_VALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w));
|
|
|
|
#endif
|
|
|
|
|
2010-06-22 02:19:04 -07:00
|
|
|
/*
|
|
|
|
* We assume that the compiler never uses sub-word alignment to store
|
2010-07-14 23:19:36 -07:00
|
|
|
* pointers and does not tag pointers on its own. Additionally, the value
|
|
|
|
* representation for all values and the jsid representation for GC-things
|
|
|
|
* do not touch the low two bits. Thus any word with the low two bits set
|
|
|
|
* is not a valid GC-thing.
|
2010-06-22 02:19:04 -07:00
|
|
|
*/
|
2010-07-14 23:19:36 -07:00
|
|
|
JS_STATIC_ASSERT(JSID_TYPE_STRING == 0 && JSID_TYPE_OBJECT == 4);
|
|
|
|
if (w & 0x3)
|
2010-08-05 05:16:56 -07:00
|
|
|
return CGCT_LOWBITSET;
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
/*
|
|
|
|
* An object jsid has its low bits tagged. In the value representation on
|
|
|
|
* 64-bit, the high bits are tagged.
|
|
|
|
*/
|
2010-08-05 05:16:56 -07:00
|
|
|
const jsuword JSID_PAYLOAD_MASK = ~jsuword(JSID_TYPE_MASK);
|
2010-07-14 23:19:36 -07:00
|
|
|
#if JS_BITS_PER_WORD == 32
|
|
|
|
jsuword payload = w & JSID_PAYLOAD_MASK;
|
|
|
|
#elif JS_BITS_PER_WORD == 64
|
|
|
|
jsuword payload = w & JSID_PAYLOAD_MASK & JSVAL_PAYLOAD_MASK;
|
|
|
|
#endif
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
Cell *cell = reinterpret_cast<Cell *>(payload);
|
|
|
|
Chunk *chunk = cell->chunk();
|
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
if (!rt->gcChunkSet.has(chunk))
|
|
|
|
return CGCT_NOTCHUNK;
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
if (!chunk->withinArenasRange(cell))
|
2010-08-05 05:16:56 -07:00
|
|
|
return CGCT_NOTARENA;
|
2010-06-24 01:30:56 -07:00
|
|
|
|
2010-10-13 11:49:22 -07:00
|
|
|
ArenaHeader *aheader = cell->arena()->header();
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
ConservativeGCTest test;
|
2010-11-17 12:39:45 -08:00
|
|
|
thingKind = aheader->thingKind;
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-11-17 12:39:45 -08:00
|
|
|
switch (thingKind) {
|
2010-10-13 11:49:22 -07:00
|
|
|
case FINALIZE_OBJECT0:
|
|
|
|
test = MarkCell<JSObject>(cell, trc);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT2:
|
|
|
|
test = MarkCell<JSObject_Slots2>(cell, trc);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT4:
|
|
|
|
test = MarkCell<JSObject_Slots4>(cell, trc);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT8:
|
|
|
|
test = MarkCell<JSObject_Slots8>(cell, trc);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT12:
|
|
|
|
test = MarkCell<JSObject_Slots12>(cell, trc);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT16:
|
|
|
|
test = MarkCell<JSObject_Slots16>(cell, trc);
|
2010-07-14 23:19:36 -07:00
|
|
|
break;
|
2010-09-24 10:54:39 -07:00
|
|
|
case FINALIZE_STRING:
|
2010-10-13 11:49:22 -07:00
|
|
|
test = MarkCell<JSString>(cell, trc);
|
2010-09-24 10:54:39 -07:00
|
|
|
break;
|
2010-11-15 12:39:00 -08:00
|
|
|
case FINALIZE_EXTERNAL_STRING:
|
|
|
|
test = MarkCell<JSExternalString>(cell, trc);
|
|
|
|
break;
|
2010-09-24 10:54:39 -07:00
|
|
|
case FINALIZE_SHORT_STRING:
|
2010-10-13 11:49:22 -07:00
|
|
|
test = MarkCell<JSShortString>(cell, trc);
|
2010-09-24 10:54:39 -07:00
|
|
|
break;
|
|
|
|
case FINALIZE_FUNCTION:
|
2010-10-13 11:49:22 -07:00
|
|
|
test = MarkCell<JSFunction>(cell, trc);
|
2010-09-24 10:54:39 -07:00
|
|
|
break;
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
case FINALIZE_XML:
|
2010-10-13 11:49:22 -07:00
|
|
|
test = MarkCell<JSXML>(cell, trc);
|
2010-09-24 10:54:39 -07:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
test = CGCT_WRONGTAG;
|
|
|
|
JS_NOT_REACHED("wrong tag");
|
2010-06-04 07:22:28 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
return test;
|
2010-08-05 05:16:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline ConservativeGCTest
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkIfGCThingWord(JSTracer *trc, jsuword w)
|
2010-08-05 05:16:56 -07:00
|
|
|
{
|
2010-11-17 12:39:45 -08:00
|
|
|
uint32 thingKind;
|
|
|
|
return MarkIfGCThingWord(trc, w, thingKind);
|
2010-08-05 05:16:56 -07:00
|
|
|
}
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
static void
|
|
|
|
MarkWordConservatively(JSTracer *trc, jsuword w)
|
|
|
|
{
|
2010-06-04 07:22:28 -07:00
|
|
|
/*
|
2010-08-05 05:16:56 -07:00
|
|
|
* The conservative scanner may access words that valgrind considers as
|
|
|
|
* undefined. To avoid false positives and not to alter valgrind view of
|
|
|
|
* the memory we make as memcheck-defined the argument, a copy of the
|
|
|
|
* original word. See bug 572678.
|
2010-06-04 07:22:28 -07:00
|
|
|
*/
|
2010-08-05 05:16:56 -07:00
|
|
|
#ifdef JS_VALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w));
|
|
|
|
#endif
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-11-17 12:39:45 -08:00
|
|
|
uint32 thingKind;
|
2010-09-24 10:54:39 -07:00
|
|
|
#if defined JS_DUMP_CONSERVATIVE_GC_ROOTS || defined JS_GCMETER
|
2010-10-07 13:43:52 -07:00
|
|
|
ConservativeGCTest test =
|
2010-09-24 10:54:39 -07:00
|
|
|
#endif
|
2010-11-17 12:39:45 -08:00
|
|
|
MarkIfGCThingWord(trc, w, thingKind);
|
2010-09-24 10:54:39 -07:00
|
|
|
|
2010-06-04 07:22:28 -07:00
|
|
|
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
|
2010-11-17 12:39:45 -08:00
|
|
|
if (test == CGCT_VALID || test == CGCT_VALIDWITHOFFSET) {
|
2010-08-05 05:16:56 -07:00
|
|
|
if (IS_GC_MARKING_TRACER(trc) && static_cast<GCMarker *>(trc)->conservativeDumpFileName) {
|
2010-11-17 12:39:45 -08:00
|
|
|
const jsuword JSID_PAYLOAD_MASK = ~jsuword(JSID_TYPE_MASK);
|
|
|
|
#if JS_BITS_PER_WORD == 32
|
|
|
|
jsuword payload = w & JSID_PAYLOAD_MASK;
|
|
|
|
#elif JS_BITS_PER_WORD == 64
|
|
|
|
jsuword payload = w & JSID_PAYLOAD_MASK & JSVAL_PAYLOAD_MASK;
|
|
|
|
#endif
|
|
|
|
void *thing = (test == CGCT_VALIDWITHOFFSET)
|
|
|
|
? GetAlignedThing((void *)payload, thingKind)
|
|
|
|
: (void *)payload;
|
|
|
|
GCMarker::ConservativeRoot root = {thing, thingKind};
|
2010-08-05 05:16:56 -07:00
|
|
|
static_cast<GCMarker *>(trc)->conservativeRoots.append(root);
|
|
|
|
}
|
2010-06-04 07:22:28 -07:00
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
#endif
|
2010-09-07 14:08:20 -07:00
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
#if defined JS_DUMP_CONSERVATIVE_GC_ROOTS || defined JS_GCMETER
|
|
|
|
if (IS_GC_MARKING_TRACER(trc))
|
|
|
|
static_cast<GCMarker *>(trc)->conservativeStats.counter[test]++;
|
2010-06-04 07:22:28 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
static void
|
2010-11-08 14:35:06 -08:00
|
|
|
MarkRangeConservatively(JSTracer *trc, const jsuword *begin, const jsuword *end)
|
2010-06-04 07:22:28 -07:00
|
|
|
{
|
|
|
|
JS_ASSERT(begin <= end);
|
2010-11-08 14:35:06 -08:00
|
|
|
for (const jsuword *i = begin; i != end; ++i)
|
2010-08-05 05:16:56 -07:00
|
|
|
MarkWordConservatively(trc, *i);
|
2010-06-04 07:22:28 -07:00
|
|
|
}
|
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
static void
|
|
|
|
MarkThreadDataConservatively(JSTracer *trc, JSThreadData *td)
|
|
|
|
{
|
|
|
|
ConservativeGCThreadData *ctd = &td->conservativeGC;
|
|
|
|
JS_ASSERT(ctd->hasStackToScan());
|
|
|
|
jsuword *stackMin, *stackEnd;
|
|
|
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
|
|
|
stackMin = td->nativeStackBase;
|
|
|
|
stackEnd = ctd->nativeStackTop;
|
|
|
|
#else
|
|
|
|
stackMin = ctd->nativeStackTop + 1;
|
|
|
|
stackEnd = td->nativeStackBase;
|
|
|
|
#endif
|
|
|
|
JS_ASSERT(stackMin <= stackEnd);
|
|
|
|
MarkRangeConservatively(trc, stackMin, stackEnd);
|
|
|
|
MarkRangeConservatively(trc, ctd->registerSnapshot.words,
|
|
|
|
JS_ARRAY_END(ctd->registerSnapshot.words));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-07-29 17:13:08 -07:00
|
|
|
void
|
2010-08-05 15:57:34 -07:00
|
|
|
MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv)
|
2010-07-29 17:13:08 -07:00
|
|
|
{
|
2010-11-08 14:35:06 -08:00
|
|
|
const jsuword *begin = beginv->payloadWord();
|
|
|
|
const jsuword *end = endv->payloadWord();;
|
2010-08-05 15:57:34 -07:00
|
|
|
#ifdef JS_NUNBOX32
|
|
|
|
/*
|
|
|
|
* With 64-bit jsvals on 32-bit systems, we can optimize a bit by
|
|
|
|
* scanning only the payloads.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(begin <= end);
|
2010-11-08 14:35:06 -08:00
|
|
|
for (const jsuword *i = begin; i != end; i += sizeof(Value)/sizeof(jsuword))
|
2010-08-05 15:57:34 -07:00
|
|
|
MarkWordConservatively(trc, *i);
|
|
|
|
#else
|
|
|
|
MarkRangeConservatively(trc, begin, end);
|
|
|
|
#endif
|
2010-07-29 17:13:08 -07:00
|
|
|
}
|
|
|
|
|
2010-06-04 07:22:28 -07:00
|
|
|
void
|
2010-08-05 05:16:56 -07:00
|
|
|
MarkConservativeStackRoots(JSTracer *trc)
|
2010-06-04 07:22:28 -07:00
|
|
|
{
|
2010-08-30 11:46:18 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
for (JSThread::Map::Range r = trc->context->runtime->threads.all(); !r.empty(); r.popFront()) {
|
|
|
|
JSThread *thread = r.front().value;
|
|
|
|
ConservativeGCThreadData *ctd = &thread->data.conservativeGC;
|
|
|
|
if (ctd->hasStackToScan()) {
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT_IF(!thread->data.requestDepth, thread->suspendCount);
|
2010-08-30 11:46:18 -07:00
|
|
|
MarkThreadDataConservatively(trc, &thread->data);
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(!thread->suspendCount);
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT(thread->data.requestDepth <= ctd->requestThreshold);
|
2010-06-04 07:22:28 -07:00
|
|
|
}
|
|
|
|
}
|
2010-08-30 11:46:18 -07:00
|
|
|
#else
|
|
|
|
MarkThreadDataConservatively(trc, &trc->context->runtime->threadData);
|
2010-09-07 14:08:20 -07:00
|
|
|
#endif
|
2010-06-04 07:22:28 -07:00
|
|
|
}
|
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
JS_NEVER_INLINE void
|
|
|
|
ConservativeGCThreadData::recordStackTop()
|
2010-06-04 07:22:28 -07:00
|
|
|
{
|
|
|
|
/* Update the native stack pointer if it points to a bigger stack. */
|
|
|
|
jsuword dummy;
|
2010-08-30 11:46:18 -07:00
|
|
|
nativeStackTop = &dummy;
|
2010-06-04 07:22:28 -07:00
|
|
|
|
2010-12-23 07:18:36 -08:00
|
|
|
/*
|
|
|
|
* To record and update the register snapshot for the conservative
|
|
|
|
* scanning with the latest values we use setjmp.
|
|
|
|
*/
|
2010-06-04 07:22:28 -07:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
# pragma warning(push)
|
|
|
|
# pragma warning(disable: 4611)
|
|
|
|
#endif
|
2010-12-23 07:18:36 -08:00
|
|
|
(void) setjmp(registerSnapshot.jmpbuf);
|
2010-06-04 07:22:28 -07:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
# pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
static inline void
|
|
|
|
RecordNativeStackTopForGC(JSContext *cx)
|
2010-06-04 07:22:28 -07:00
|
|
|
{
|
2010-08-30 11:46:18 -07:00
|
|
|
ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC;
|
2010-09-07 14:08:20 -07:00
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/* Record the stack top here only if we are called from a request. */
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT(cx->thread->data.requestDepth >= ctd->requestThreshold);
|
|
|
|
if (cx->thread->data.requestDepth == ctd->requestThreshold)
|
2010-08-30 11:46:18 -07:00
|
|
|
return;
|
2010-06-04 07:22:28 -07:00
|
|
|
#endif
|
2010-08-30 11:46:18 -07:00
|
|
|
ctd->recordStackTop();
|
2010-06-04 07:22:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
} /* namespace js */
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
static void
|
|
|
|
CheckLeakedRoots(JSRuntime *rt);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
js_FinishGC(JSRuntime *rt)
|
|
|
|
{
|
2010-07-11 00:09:34 -07:00
|
|
|
#ifdef JS_ARENAMETER
|
|
|
|
JS_DumpArenaStats(stdout);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
#ifdef JS_GCMETER
|
2009-12-24 01:31:07 -08:00
|
|
|
if (JS_WANT_GC_METER_PRINT)
|
|
|
|
js_DumpGCStats(rt, stdout);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2010-09-27 15:35:10 -07:00
|
|
|
|
2011-02-04 10:59:07 -08:00
|
|
|
/* Delete all remaining Compartments. */
|
2010-09-24 10:54:39 -07:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
|
|
|
|
JSCompartment *comp = *c;
|
|
|
|
comp->finishArenaLists();
|
2011-01-17 19:44:10 -08:00
|
|
|
js_delete(comp);
|
2010-09-24 10:54:39 -07:00
|
|
|
}
|
|
|
|
rt->compartments.clear();
|
2011-01-13 14:42:36 -08:00
|
|
|
rt->atomsCompartment = NULL;
|
2010-09-24 10:54:39 -07:00
|
|
|
|
|
|
|
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
|
|
|
ReleaseGCChunk(rt, r.front());
|
|
|
|
rt->gcChunkSet.clear();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-04-27 12:40:28 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-09-07 14:08:20 -07:00
|
|
|
rt->gcHelperThread.finish(rt);
|
2010-04-27 12:40:28 -07:00
|
|
|
#endif
|
2010-04-22 23:58:44 -07:00
|
|
|
|
2010-06-22 02:19:04 -07:00
|
|
|
#ifdef DEBUG
|
2010-05-20 13:50:08 -07:00
|
|
|
if (!rt->gcRootsHash.empty())
|
2007-03-22 10:30:00 -07:00
|
|
|
CheckLeakedRoots(rt);
|
|
|
|
#endif
|
2010-05-20 13:50:08 -07:00
|
|
|
rt->gcRootsHash.clear();
|
|
|
|
rt->gcLocksHash.clear();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
2010-07-14 23:19:36 -07:00
|
|
|
js_AddRoot(JSContext *cx, Value *vp, const char *name)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-07-14 23:19:36 -07:00
|
|
|
JSBool ok = js_AddRootRT(cx->runtime, Jsvalify(vp), name);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!ok)
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
2010-06-07 17:05:02 -07:00
|
|
|
js_AddGCThingRoot(JSContext *cx, void **rp, const char *name)
|
|
|
|
{
|
|
|
|
JSBool ok = js_AddGCThingRootRT(cx->runtime, rp, name);
|
|
|
|
if (!ok)
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
JS_FRIEND_API(JSBool)
|
|
|
|
js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Due to the long-standing, but now removed, use of rt->gcLock across the
|
|
|
|
* bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
|
|
|
|
* properly with a racing GC, without calling JS_AddRoot from a request.
|
|
|
|
* We have to preserve API compatibility here, now that we avoid holding
|
|
|
|
* rt->gcLock across the mark phase (including the root hashtable mark).
|
|
|
|
*/
|
2010-04-08 05:54:18 -07:00
|
|
|
AutoLockGC lock(rt);
|
2009-02-06 20:05:32 -08:00
|
|
|
js_WaitForGC(rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
return !!rt->gcRootsHash.put((void *)vp,
|
|
|
|
RootInfo(name, JS_GC_ROOT_VALUE_PTR));
|
2010-06-07 17:05:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
|
|
js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
|
|
|
|
{
|
2010-07-14 23:19:36 -07:00
|
|
|
/*
|
|
|
|
* Due to the long-standing, but now removed, use of rt->gcLock across the
|
|
|
|
* bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
|
|
|
|
* properly with a racing GC, without calling JS_AddRoot from a request.
|
|
|
|
* We have to preserve API compatibility here, now that we avoid holding
|
|
|
|
* rt->gcLock across the mark phase (including the root hashtable mark).
|
|
|
|
*/
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
js_WaitForGC(rt);
|
|
|
|
|
|
|
|
return !!rt->gcRootsHash.put((void *)rp,
|
|
|
|
RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
|
2010-06-07 17:05:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JS_FRIEND_API(JSBool)
|
2007-03-22 10:30:00 -07:00
|
|
|
js_RemoveRoot(JSRuntime *rt, void *rp)
|
|
|
|
{
|
|
|
|
/*
|
2010-07-14 23:19:36 -07:00
|
|
|
* Due to the JS_RemoveRootRT API, we may be called outside of a request.
|
2007-03-22 10:30:00 -07:00
|
|
|
* Same synchronization drill as above in js_AddRoot.
|
|
|
|
*/
|
2010-04-08 05:54:18 -07:00
|
|
|
AutoLockGC lock(rt);
|
2009-02-06 20:05:32 -08:00
|
|
|
js_WaitForGC(rt);
|
2010-05-20 13:50:08 -07:00
|
|
|
rt->gcRootsHash.remove(rp);
|
2007-03-22 10:30:00 -07:00
|
|
|
rt->gcPoke = JS_TRUE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
typedef RootedValueMap::Range RootRange;
|
|
|
|
typedef RootedValueMap::Entry RootEntry;
|
|
|
|
typedef RootedValueMap::Enum RootEnum;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
static void
|
|
|
|
CheckLeakedRoots(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
uint32 leakedroots = 0;
|
|
|
|
|
|
|
|
/* Warn (but don't assert) debug builds of any remaining roots. */
|
2010-07-14 23:19:36 -07:00
|
|
|
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) {
|
|
|
|
RootEntry &entry = r.front();
|
2010-05-20 13:50:08 -07:00
|
|
|
leakedroots++;
|
|
|
|
fprintf(stderr,
|
|
|
|
"JS engine warning: leaking GC root \'%s\' at %p\n",
|
2010-07-14 23:19:36 -07:00
|
|
|
entry.value.name ? entry.value.name : "", entry.key);
|
2010-05-20 13:50:08 -07:00
|
|
|
}
|
2010-07-14 23:19:36 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (leakedroots > 0) {
|
|
|
|
if (leakedroots == 1) {
|
|
|
|
fprintf(stderr,
|
2008-02-16 22:56:40 -08:00
|
|
|
"JS engine warning: 1 GC root remains after destroying the JSRuntime at %p.\n"
|
2007-03-22 10:30:00 -07:00
|
|
|
" This root may point to freed memory. Objects reachable\n"
|
2008-02-26 13:01:42 -08:00
|
|
|
" through it have not been finalized.\n",
|
|
|
|
(void *) rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
fprintf(stderr,
|
2008-02-16 22:56:40 -08:00
|
|
|
"JS engine warning: %lu GC roots remain after destroying the JSRuntime at %p.\n"
|
2007-03-22 10:30:00 -07:00
|
|
|
" These roots may point to freed memory. Objects reachable\n"
|
|
|
|
" through them have not been finalized.\n",
|
2008-02-26 13:01:42 -08:00
|
|
|
(unsigned long) leakedroots, (void *) rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_DumpNamedRoots(JSRuntime *rt,
|
2010-07-14 23:19:36 -07:00
|
|
|
void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
|
2007-03-22 10:30:00 -07:00
|
|
|
void *data)
|
|
|
|
{
|
2010-07-14 23:19:36 -07:00
|
|
|
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) {
|
|
|
|
RootEntry &entry = r.front();
|
|
|
|
if (const char *name = entry.value.name)
|
|
|
|
dump(name, entry.key, entry.value.type, data);
|
2010-05-20 13:50:08 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
|
|
uint32
|
|
|
|
js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
|
|
|
|
{
|
2010-04-08 05:54:18 -07:00
|
|
|
AutoLockGC lock(rt);
|
2010-07-14 23:19:36 -07:00
|
|
|
int ct = 0;
|
|
|
|
for (RootEnum e(rt->gcRootsHash); !e.empty(); e.popFront()) {
|
|
|
|
RootEntry &entry = e.front();
|
|
|
|
|
|
|
|
ct++;
|
|
|
|
intN mapflags = map(entry.key, entry.value.type, entry.value.name, data);
|
2010-05-20 13:50:08 -07:00
|
|
|
|
|
|
|
if (mapflags & JS_MAP_GCROOT_REMOVE)
|
|
|
|
e.removeFront();
|
|
|
|
if (mapflags & JS_MAP_GCROOT_STOP)
|
|
|
|
break;
|
|
|
|
}
|
2010-07-14 23:19:36 -07:00
|
|
|
|
|
|
|
return ct;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-08-25 14:42:42 -07:00
|
|
|
void
|
|
|
|
JSRuntime::setGCTriggerFactor(uint32 factor)
|
|
|
|
{
|
|
|
|
JS_ASSERT(factor >= 100);
|
|
|
|
|
|
|
|
gcTriggerFactor = factor;
|
|
|
|
setGCLastBytes(gcLastBytes);
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
for (JSCompartment **c = compartments.begin(); c != compartments.end(); ++c) {
|
|
|
|
(*c)->setGCLastBytes(gcLastBytes);
|
|
|
|
}
|
2009-08-25 14:42:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
JSRuntime::setGCLastBytes(size_t lastBytes)
|
|
|
|
{
|
|
|
|
gcLastBytes = lastBytes;
|
2010-10-07 13:43:52 -07:00
|
|
|
|
|
|
|
/* FIXME bug 603916 - we should unify the triggers here. */
|
|
|
|
float trigger1 = float(lastBytes) * float(gcTriggerFactor) / 100.0f;
|
|
|
|
float trigger2 = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) *
|
|
|
|
GC_HEAP_GROWTH_FACTOR;
|
2011-02-07 11:24:08 -08:00
|
|
|
float maxtrigger = Max(trigger1, trigger2);
|
|
|
|
gcTriggerBytes = (float(gcMaxBytes) < maxtrigger) ? gcMaxBytes : size_t(maxtrigger);
|
2009-08-25 14:42:42 -07:00
|
|
|
}
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
void
|
|
|
|
JSCompartment::setGCLastBytes(size_t lastBytes)
|
|
|
|
{
|
|
|
|
gcLastBytes = lastBytes;
|
|
|
|
|
|
|
|
/* FIXME bug 603916 - we should unify the triggers here. */
|
|
|
|
float trigger1 = float(lastBytes) * float(rt->gcTriggerFactor) / 100.0f;
|
|
|
|
float trigger2 = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) *
|
|
|
|
GC_HEAP_GROWTH_FACTOR;
|
2011-02-07 11:24:08 -08:00
|
|
|
float maxtrigger = Max(trigger1, trigger2);
|
|
|
|
gcTriggerBytes = (float(rt->gcMaxBytes) < maxtrigger) ? rt->gcMaxBytes : size_t(maxtrigger);
|
2011-01-07 23:44:57 -08:00
|
|
|
}
|
|
|
|
|
2009-10-15 23:10:54 -07:00
|
|
|
void
|
2010-09-24 10:54:39 -07:00
|
|
|
FreeLists::purge()
|
2009-10-15 23:10:54 -07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Return the free list back to the arena so the GC finalization will not
|
|
|
|
* run the finalizers over unitialized bytes from free things.
|
|
|
|
*/
|
2010-09-24 10:54:39 -07:00
|
|
|
for (FreeCell ***p = finalizables; p != JS_ARRAY_END(finalizables); ++p)
|
|
|
|
*p = NULL;
|
2009-11-12 03:53:25 -08:00
|
|
|
}
|
|
|
|
|
2010-10-13 11:49:22 -07:00
|
|
|
ArenaList *
|
|
|
|
GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) {
|
|
|
|
JS_ASSERT(thingKind < FINALIZE_LIMIT);
|
|
|
|
return &c->arenas[thingKind];
|
2010-07-28 11:20:19 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
bool
|
|
|
|
CheckAllocation(JSContext *cx)
|
2009-10-15 23:10:54 -07:00
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_ASSERT(cx->thread);
|
2010-09-24 10:54:39 -07:00
|
|
|
#endif
|
|
|
|
JS_ASSERT(!cx->runtime->gcRunning);
|
|
|
|
return true;
|
|
|
|
}
|
2009-10-15 23:10:54 -07:00
|
|
|
#endif
|
|
|
|
|
2010-10-07 13:43:52 -07:00
|
|
|
inline bool
|
|
|
|
NeedLastDitchGC(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
if (rt->gcZeal >= 1)
|
|
|
|
return true;
|
|
|
|
#endif
|
2011-01-07 23:44:57 -08:00
|
|
|
return rt->gcIsNeeded;
|
2010-10-07 13:43:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return false only if the GC run but could not bring its memory usage under
|
|
|
|
* JSRuntime::gcMaxBytes.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
RunLastDitchGC(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
METER(rt->gcStats.lastditch++);
|
|
|
|
#ifdef JS_THREADSAFE
|
2011-01-13 14:42:36 -08:00
|
|
|
Conditionally<AutoUnlockAtomsCompartment>
|
|
|
|
unlockAtomsCompartmenIf(cx->compartment == rt->atomsCompartment &&
|
|
|
|
rt->atomsCompartmentIsLocked, cx);
|
2010-10-07 13:43:52 -07:00
|
|
|
#endif
|
|
|
|
/* The last ditch GC preserves all atoms. */
|
|
|
|
AutoKeepAtoms keep(rt);
|
2011-01-07 23:44:57 -08:00
|
|
|
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL);
|
2010-10-07 13:43:52 -07:00
|
|
|
|
|
|
|
return rt->gcBytes < rt->gcMaxBytes;
|
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template <typename T>
|
2010-10-13 11:49:22 -07:00
|
|
|
inline bool
|
|
|
|
RefillTypedFreeList(JSContext *cx, unsigned thingKind)
|
2010-09-24 10:54:39 -07:00
|
|
|
{
|
|
|
|
JSCompartment *compartment = cx->compartment;
|
|
|
|
JS_ASSERT_IF(compartment->freeLists.finalizables[thingKind],
|
|
|
|
!*compartment->freeLists.finalizables[thingKind]);
|
2009-11-12 03:53:25 -08:00
|
|
|
|
2010-10-07 13:43:52 -07:00
|
|
|
JS_ASSERT(!cx->runtime->gcRunning);
|
|
|
|
if (cx->runtime->gcRunning)
|
2010-09-24 10:54:39 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
|
|
|
|
do {
|
2010-10-07 13:43:52 -07:00
|
|
|
if (canGC && JS_UNLIKELY(NeedLastDitchGC(cx))) {
|
|
|
|
if (!RunLastDitchGC(cx))
|
|
|
|
break;
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
/*
|
|
|
|
* The JSGC_END callback can legitimately allocate new GC
|
|
|
|
* things and populate the free list. If that happens, just
|
|
|
|
* return that list head.
|
|
|
|
*/
|
|
|
|
if (compartment->freeLists.finalizables[thingKind])
|
|
|
|
return true;
|
2010-10-07 13:43:52 -07:00
|
|
|
canGC = false;
|
2010-09-24 10:54:39 -07:00
|
|
|
}
|
2010-10-07 13:43:52 -07:00
|
|
|
|
|
|
|
ArenaList *arenaList = GetFinalizableArenaList(compartment, thingKind);
|
|
|
|
Arena<T> *a = reinterpret_cast<Arena<T> *>(arenaList->getNextWithFreeList());
|
|
|
|
if (a) {
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(a->header()->freeList);
|
2010-10-13 11:49:22 -07:00
|
|
|
JS_ASSERT(sizeof(T) == a->header()->thingSize);
|
2010-09-24 10:54:39 -07:00
|
|
|
compartment->freeLists.populate(a, thingKind);
|
|
|
|
return true;
|
|
|
|
}
|
2010-10-07 13:43:52 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the allocation fails rt->gcIsNeeded will be set and we will run
|
|
|
|
* the GC on the next loop iteration if the last ditch GC is allowed.
|
|
|
|
*/
|
2010-09-24 10:54:39 -07:00
|
|
|
a = AllocateArena<T>(cx, thingKind);
|
|
|
|
if (a) {
|
|
|
|
compartment->freeLists.populate(a, thingKind);
|
2010-10-13 11:49:22 -07:00
|
|
|
arenaList->insert((Arena<FreeCell> *) a);
|
2010-09-24 10:54:39 -07:00
|
|
|
a->getMarkingDelay()->init();
|
|
|
|
return true;
|
|
|
|
}
|
2010-10-07 13:43:52 -07:00
|
|
|
} while (canGC);
|
|
|
|
|
|
|
|
METER(cx->runtime->gcStats.fail++);
|
|
|
|
js_ReportOutOfMemory(cx);
|
|
|
|
return false;
|
2010-09-24 10:54:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-10-13 11:49:22 -07:00
|
|
|
RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
|
|
|
|
{
|
|
|
|
switch (thingKind) {
|
|
|
|
case FINALIZE_OBJECT0:
|
|
|
|
return RefillTypedFreeList<JSObject>(cx, thingKind);
|
|
|
|
case FINALIZE_OBJECT2:
|
|
|
|
return RefillTypedFreeList<JSObject_Slots2>(cx, thingKind);
|
|
|
|
case FINALIZE_OBJECT4:
|
|
|
|
return RefillTypedFreeList<JSObject_Slots4>(cx, thingKind);
|
|
|
|
case FINALIZE_OBJECT8:
|
|
|
|
return RefillTypedFreeList<JSObject_Slots8>(cx, thingKind);
|
|
|
|
case FINALIZE_OBJECT12:
|
|
|
|
return RefillTypedFreeList<JSObject_Slots12>(cx, thingKind);
|
|
|
|
case FINALIZE_OBJECT16:
|
|
|
|
return RefillTypedFreeList<JSObject_Slots16>(cx, thingKind);
|
|
|
|
case FINALIZE_STRING:
|
|
|
|
return RefillTypedFreeList<JSString>(cx, thingKind);
|
2010-11-15 12:39:00 -08:00
|
|
|
case FINALIZE_EXTERNAL_STRING:
|
|
|
|
return RefillTypedFreeList<JSExternalString>(cx, thingKind);
|
2010-10-13 11:49:22 -07:00
|
|
|
case FINALIZE_SHORT_STRING:
|
|
|
|
return RefillTypedFreeList<JSShortString>(cx, thingKind);
|
|
|
|
case FINALIZE_FUNCTION:
|
|
|
|
return RefillTypedFreeList<JSFunction>(cx, thingKind);
|
2010-09-24 10:54:39 -07:00
|
|
|
#if JS_HAS_XML_SUPPORT
|
2010-10-13 11:49:22 -07:00
|
|
|
case FINALIZE_XML:
|
|
|
|
return RefillTypedFreeList<JSXML>(cx, thingKind);
|
2010-09-24 10:54:39 -07:00
|
|
|
#endif
|
2010-10-13 11:49:22 -07:00
|
|
|
default:
|
|
|
|
JS_NOT_REACHED("bad finalize kind");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
|
|
|
|
uint32
|
2011-03-14 13:59:53 -07:00
|
|
|
js_GetGCThingTraceKind(void *thing)
|
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
return GetGCThingTraceKind(thing);
|
2009-10-15 02:53:40 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSBool
|
|
|
|
js_LockGCThingRT(JSRuntime *rt, void *thing)
|
|
|
|
{
|
2010-05-20 13:50:08 -07:00
|
|
|
GCLocks *locks;
|
2010-06-22 02:19:04 -07:00
|
|
|
|
2009-09-10 04:13:59 -07:00
|
|
|
if (!thing)
|
2009-12-24 01:31:07 -08:00
|
|
|
return true;
|
2010-05-20 13:50:08 -07:00
|
|
|
locks = &rt->gcLocksHash;
|
2010-04-08 05:54:18 -07:00
|
|
|
AutoLockGC lock(rt);
|
2010-05-20 13:50:08 -07:00
|
|
|
GCLocks::AddPtr p = locks->lookupForAdd(thing);
|
|
|
|
|
|
|
|
if (!p) {
|
|
|
|
if (!locks->add(p, thing, 1))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(p->value >= 1);
|
|
|
|
p->value++;
|
2008-02-24 06:14:45 -08:00
|
|
|
}
|
2010-05-20 13:50:08 -07:00
|
|
|
|
|
|
|
METER(rt->gcStats.lock++);
|
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-13 23:55:17 -08:00
|
|
|
void
|
2007-03-22 10:30:00 -07:00
|
|
|
js_UnlockGCThingRT(JSRuntime *rt, void *thing)
|
|
|
|
{
|
2009-09-10 04:13:59 -07:00
|
|
|
if (!thing)
|
2009-12-13 23:55:17 -08:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-04-08 05:54:18 -07:00
|
|
|
AutoLockGC lock(rt);
|
2010-05-20 13:50:08 -07:00
|
|
|
GCLocks::Ptr p = rt->gcLocksHash.lookup(thing);
|
|
|
|
|
|
|
|
if (p) {
|
2009-12-24 01:31:07 -08:00
|
|
|
rt->gcPoke = true;
|
2010-05-20 13:50:08 -07:00
|
|
|
if (--p->value == 0)
|
|
|
|
rt->gcLocksHash.remove(p);
|
|
|
|
|
2009-12-24 01:31:07 -08:00
|
|
|
METER(rt->gcStats.unlock++);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-16 23:53:37 -07:00
|
|
|
JS_PUBLIC_API(void)
|
|
|
|
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
|
|
|
|
{
|
|
|
|
switch (kind) {
|
2009-08-27 22:53:26 -07:00
|
|
|
case JSTRACE_OBJECT: {
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkChildren(trc, (JSObject *)thing);
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
2009-08-27 22:53:26 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-08-27 22:53:26 -07:00
|
|
|
case JSTRACE_STRING: {
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkChildren(trc, (JSString *)thing);
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
2009-08-27 22:53:26 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
2007-04-16 23:53:37 -07:00
|
|
|
case JSTRACE_XML:
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkChildren(trc, (JSXML *)thing);
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-26 11:44:04 -07:00
|
|
|
namespace js {
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
2009-12-13 23:55:17 -08:00
|
|
|
* When the native stack is low, the GC does not call JS_TraceChildren to mark
|
|
|
|
* the reachable "children" of the thing. Rather the thing is put aside and
|
|
|
|
* JS_TraceChildren is called later with more space on the C stack.
|
|
|
|
*
|
|
|
|
* To implement such delayed marking of the children with minimal overhead for
|
2010-09-24 10:54:39 -07:00
|
|
|
* the normal case of sufficient native stack, the code adds a field per
|
2010-10-07 13:43:52 -07:00
|
|
|
* arena. The field marlingdelay->link links all arenas with delayed things
|
|
|
|
* into a stack list with the pointer to stack top in
|
2010-09-24 10:54:39 -07:00
|
|
|
* GCMarker::unmarkedArenaStackTop. delayMarkingChildren adds
|
2010-07-26 11:44:04 -07:00
|
|
|
* arenas to the stack as necessary while markDelayedChildren pops the arenas
|
2010-04-12 13:59:19 -07:00
|
|
|
* from the stack until it empties.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
GCMarker::GCMarker(JSContext *cx)
|
2010-09-24 10:54:39 -07:00
|
|
|
: color(0), stackLimit(0), unmarkedArenaStackTop(NULL)
|
2010-08-05 05:16:56 -07:00
|
|
|
{
|
|
|
|
JS_TRACER_INIT(this, cx, NULL);
|
|
|
|
#ifdef DEBUG
|
|
|
|
markLaterCount = 0;
|
|
|
|
#endif
|
|
|
|
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
|
|
|
|
conservativeDumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS");
|
|
|
|
memset(&conservativeStats, 0, sizeof(conservativeStats));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
GCMarker::~GCMarker()
|
|
|
|
{
|
|
|
|
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
|
|
|
|
dumpConservativeRoots();
|
|
|
|
#endif
|
|
|
|
#ifdef JS_GCMETER
|
|
|
|
/* Update total stats. */
|
|
|
|
context->runtime->gcStats.conservative.add(conservativeStats);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-07-26 11:44:04 -07:00
|
|
|
void
|
2011-03-23 11:57:15 -07:00
|
|
|
GCMarker::delayMarkingChildren(const void *thing)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-03-23 11:57:15 -07:00
|
|
|
const Cell *cell = reinterpret_cast<const Cell *>(thing);
|
2010-09-24 10:54:39 -07:00
|
|
|
Arena<Cell> *a = cell->arena();
|
|
|
|
JS_ASSERT(cell->isMarked());
|
|
|
|
METER(cell->compartment()->rt->gcStats.unmarked++);
|
|
|
|
MarkingDelay *markingDelay = a->getMarkingDelay();
|
2010-04-12 13:59:19 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
if (markingDelay->link) {
|
|
|
|
if (markingDelay->start > (jsuword)cell)
|
|
|
|
markingDelay->start = (jsuword)cell;
|
|
|
|
/* Arena already scheduled to be marked again */
|
|
|
|
return;
|
2007-09-16 06:03:17 -07:00
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
markingDelay->start = (jsuword)cell;
|
|
|
|
Arena<Cell> *tos = unmarkedArenaStackTop;
|
|
|
|
markingDelay->link = tos ? tos : a;
|
|
|
|
unmarkedArenaStackTop = a;
|
2009-12-13 23:55:17 -08:00
|
|
|
#ifdef DEBUG
|
2010-09-24 10:54:39 -07:00
|
|
|
JSCompartment *comp = cell->compartment();
|
|
|
|
markLaterCount += Arena<FreeCell>::ThingsPerArena;
|
|
|
|
METER_UPDATE_MAX(comp->rt->gcStats.maxunmarked, markLaterCount);
|
2009-12-13 23:55:17 -08:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template<typename T>
|
|
|
|
void
|
|
|
|
Arena<T>::markDelayedChildren(JSTracer *trc)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
T* thing = (T *)getMarkingDelay()->start;
|
|
|
|
T *thingsEnd = &t.things[ThingsPerArena-1].t;
|
|
|
|
JS_ASSERT(thing == getAlignedThing(thing));
|
|
|
|
while (thing <= thingsEnd) {
|
2011-03-14 13:59:53 -07:00
|
|
|
if (thing->isMarked())
|
2011-03-22 14:19:09 -07:00
|
|
|
js::gc::MarkChildren(trc, thing);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
thing++;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
void
|
|
|
|
GCMarker::markDelayedChildren()
|
|
|
|
{
|
|
|
|
while (Arena<Cell> *a = unmarkedArenaStackTop) {
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
2010-10-08 16:25:57 -07:00
|
|
|
* markingDelay->link == current arena indicates last arena on stack.
|
|
|
|
* If marking gets delayed at the same arena again, the arena is pushed
|
|
|
|
* again in delayMarkingChildren. markingDelay->link has to be cleared,
|
|
|
|
* otherwise the arena is not pushed again.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkingDelay *markingDelay = a->getMarkingDelay();
|
2010-10-08 16:25:57 -07:00
|
|
|
unmarkedArenaStackTop = (markingDelay->link != a)
|
|
|
|
? markingDelay->link
|
|
|
|
: NULL;
|
|
|
|
markingDelay->link = NULL;
|
|
|
|
#ifdef DEBUG
|
|
|
|
markLaterCount -= Arena<FreeCell>::ThingsPerArena;
|
|
|
|
#endif
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
switch (a->header()->thingKind) {
|
2010-10-13 11:49:22 -07:00
|
|
|
case FINALIZE_OBJECT0:
|
2010-09-24 10:54:39 -07:00
|
|
|
reinterpret_cast<Arena<JSObject> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
2010-10-13 11:49:22 -07:00
|
|
|
case FINALIZE_OBJECT2:
|
|
|
|
reinterpret_cast<Arena<JSObject_Slots2> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT4:
|
|
|
|
reinterpret_cast<Arena<JSObject_Slots4> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT8:
|
|
|
|
reinterpret_cast<Arena<JSObject_Slots8> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT12:
|
|
|
|
reinterpret_cast<Arena<JSObject_Slots12> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
|
|
|
case FINALIZE_OBJECT16:
|
|
|
|
reinterpret_cast<Arena<JSObject_Slots16> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
2010-09-24 10:54:39 -07:00
|
|
|
case FINALIZE_STRING:
|
|
|
|
reinterpret_cast<Arena<JSString> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
2010-11-15 12:39:00 -08:00
|
|
|
case FINALIZE_EXTERNAL_STRING:
|
|
|
|
reinterpret_cast<Arena<JSExternalString> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
2010-09-24 10:54:39 -07:00
|
|
|
case FINALIZE_SHORT_STRING:
|
|
|
|
JS_ASSERT(false);
|
|
|
|
break;
|
|
|
|
case FINALIZE_FUNCTION:
|
|
|
|
reinterpret_cast<Arena<JSFunction> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
case FINALIZE_XML:
|
|
|
|
reinterpret_cast<Arena<JSXML> *>(a)->markDelayedChildren(this);
|
|
|
|
break;
|
2009-12-13 23:55:17 -08:00
|
|
|
#endif
|
2010-09-24 10:54:39 -07:00
|
|
|
default:
|
2010-10-08 16:25:57 -07:00
|
|
|
JS_NOT_REACHED("wrong thingkind");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2010-07-26 11:44:04 -07:00
|
|
|
JS_ASSERT(markLaterCount == 0);
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(!unmarkedArenaStackTop);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
} /* namespace js */
|
|
|
|
|
2010-05-20 13:50:08 -07:00
|
|
|
static void
|
2010-07-14 23:19:36 -07:00
|
|
|
gc_root_traversal(JSTracer *trc, const RootEntry &entry)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
2010-07-14 23:19:36 -07:00
|
|
|
void *ptr;
|
|
|
|
if (entry.value.type == JS_GC_ROOT_GCTHING_PTR) {
|
|
|
|
ptr = *reinterpret_cast<void **>(entry.key);
|
|
|
|
} else {
|
|
|
|
Value *vp = reinterpret_cast<Value *>(entry.key);
|
2010-09-24 10:54:39 -07:00
|
|
|
ptr = vp->isGCThing() ? vp->toGCThing() : NULL;
|
2010-07-14 23:19:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr) {
|
2011-03-14 13:59:53 -07:00
|
|
|
if (!JSAtom::isStatic(ptr)) {
|
2009-12-13 23:55:17 -08:00
|
|
|
bool root_points_to_gcArenaList = false;
|
2010-09-24 10:54:39 -07:00
|
|
|
JSCompartment **c = trc->context->runtime->compartments.begin();
|
|
|
|
for (; c != trc->context->runtime->compartments.end(); ++c) {
|
|
|
|
JSCompartment *comp = *c;
|
|
|
|
if (checkArenaListsForThing(comp, ptr)) {
|
|
|
|
root_points_to_gcArenaList = true;
|
|
|
|
break;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2010-07-14 23:19:36 -07:00
|
|
|
if (!root_points_to_gcArenaList && entry.value.name) {
|
2009-12-13 23:55:17 -08:00
|
|
|
fprintf(stderr,
|
2007-03-22 10:30:00 -07:00
|
|
|
"JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n"
|
2010-07-14 23:19:36 -07:00
|
|
|
"invalid gcthing. This is usually caused by a missing call to JS_RemoveRoot.\n"
|
2007-03-22 10:30:00 -07:00
|
|
|
"The root's name is \"%s\".\n",
|
2010-07-14 23:19:36 -07:00
|
|
|
entry.value.name);
|
2009-12-13 23:55:17 -08:00
|
|
|
}
|
|
|
|
JS_ASSERT(root_points_to_gcArenaList);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2010-07-14 23:19:36 -07:00
|
|
|
#endif
|
|
|
|
JS_SET_TRACING_NAME(trc, entry.value.name ? entry.value.name : "root");
|
|
|
|
if (entry.value.type == JS_GC_ROOT_GCTHING_PTR)
|
|
|
|
MarkGCThing(trc, *reinterpret_cast<void **>(entry.key));
|
|
|
|
else
|
|
|
|
MarkValueRaw(trc, *reinterpret_cast<Value *>(entry.key));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-05-20 13:50:08 -07:00
|
|
|
static void
|
|
|
|
gc_lock_traversal(const GCLocks::Entry &entry, JSTracer *trc)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-05-20 13:50:08 -07:00
|
|
|
JS_ASSERT(entry.value >= 1);
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkGCThing(trc, entry.key, "locked object");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-04-16 23:53:37 -07:00
|
|
|
js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, fp->scopeChain(), "scope chain");
|
|
|
|
if (fp->isDummyFrame())
|
|
|
|
return;
|
|
|
|
|
2010-08-12 15:46:03 -07:00
|
|
|
if (fp->hasCallObj())
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, fp->callObj(), "call");
|
2010-08-12 15:46:03 -07:00
|
|
|
if (fp->hasArgsObj())
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, fp->argsObj(), "arguments");
|
2010-12-17 16:33:04 -08:00
|
|
|
if (fp->isScriptFrame()) {
|
2010-08-09 22:43:33 -07:00
|
|
|
js_TraceScript(trc, fp->script());
|
2010-12-17 16:33:04 -08:00
|
|
|
fp->script()->compartment->active = true;
|
|
|
|
}
|
2008-11-27 02:16:30 -08:00
|
|
|
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkValue(trc, fp->returnValue(), "rval");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
void
|
|
|
|
AutoIdArray::trace(JSTracer *trc)
|
|
|
|
{
|
|
|
|
JS_ASSERT(tag == IDARRAY);
|
|
|
|
gc::MarkIdRange(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AutoEnumStateRooter::trace(JSTracer *trc)
|
|
|
|
{
|
|
|
|
js::gc::MarkObject(trc, *obj, "js::AutoEnumStateRooter.obj");
|
|
|
|
}
|
|
|
|
|
2010-06-16 14:13:01 -07:00
|
|
|
inline void
|
|
|
|
AutoGCRooter::trace(JSTracer *trc)
|
|
|
|
{
|
|
|
|
switch (tag) {
|
|
|
|
case JSVAL:
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkValue(trc, static_cast<AutoValueRooter *>(this)->val, "js::AutoValueRooter.val");
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
case SHAPE:
|
|
|
|
static_cast<AutoShapeRooter *>(this)->shape->trace(trc);
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
case PARSER:
|
|
|
|
static_cast<Parser *>(this)->trace(trc);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SCRIPT:
|
|
|
|
if (JSScript *script = static_cast<AutoScriptRooter *>(this)->script)
|
|
|
|
js_TraceScript(trc, script);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ENUMERATOR:
|
|
|
|
static_cast<AutoEnumStateRooter *>(this)->trace(trc);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IDARRAY: {
|
|
|
|
JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkIdRange(trc, ida->length, ida->vector, "js::AutoIdArray.idArray");
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DESCRIPTORS: {
|
2010-07-14 23:19:36 -07:00
|
|
|
PropDescArray &descriptors =
|
|
|
|
static_cast<AutoPropDescArrayRooter *>(this)->descriptors;
|
2010-06-16 14:13:01 -07:00
|
|
|
for (size_t i = 0, len = descriptors.length(); i < len; i++) {
|
2010-07-14 23:19:36 -07:00
|
|
|
PropDesc &desc = descriptors[i];
|
|
|
|
MarkValue(trc, desc.pd, "PropDesc::pd");
|
|
|
|
MarkValue(trc, desc.value, "PropDesc::value");
|
|
|
|
MarkValue(trc, desc.get, "PropDesc::get");
|
|
|
|
MarkValue(trc, desc.set, "PropDesc::set");
|
2010-06-16 14:13:01 -07:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DESCRIPTOR : {
|
2010-07-14 23:19:36 -07:00
|
|
|
PropertyDescriptor &desc = *static_cast<AutoPropertyDescriptorRooter *>(this);
|
2010-06-16 14:13:01 -07:00
|
|
|
if (desc.obj)
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, *desc.obj, "Descriptor::obj");
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkValue(trc, desc.value, "Descriptor::value");
|
2010-07-17 01:51:07 -07:00
|
|
|
if ((desc.attrs & JSPROP_GETTER) && desc.getter)
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, *CastAsObject(desc.getter), "Descriptor::get");
|
2010-07-17 01:51:07 -07:00
|
|
|
if (desc.attrs & JSPROP_SETTER && desc.setter)
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, *CastAsObject(desc.setter), "Descriptor::set");
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NAMESPACES: {
|
2010-06-19 11:58:00 -07:00
|
|
|
JSXMLArray &array = static_cast<AutoNamespaceArray *>(this)->array;
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkObjectRange(trc, array.length, reinterpret_cast<JSObject **>(array.vector),
|
|
|
|
"JSXMLArray.vector");
|
2010-06-16 14:13:01 -07:00
|
|
|
array.cursors->trace(trc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case XML:
|
|
|
|
js_TraceXML(trc, static_cast<AutoXMLRooter *>(this)->xml);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OBJECT:
|
2010-07-14 23:19:36 -07:00
|
|
|
if (JSObject *obj = static_cast<AutoObjectRooter *>(this)->obj)
|
2010-08-09 22:43:33 -07:00
|
|
|
MarkObject(trc, *obj, "js::AutoObjectRooter.obj");
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
case ID:
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkId(trc, static_cast<AutoIdRooter *>(this)->id_, "js::AutoIdRooter.val");
|
|
|
|
return;
|
|
|
|
|
|
|
|
case VALVECTOR: {
|
|
|
|
Vector<Value, 8> &vector = static_cast<js::AutoValueVector *>(this)->vector;
|
|
|
|
MarkValueRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case STRING:
|
|
|
|
if (JSString *str = static_cast<js::AutoStringRooter *>(this)->str)
|
|
|
|
MarkString(trc, str, "js::AutoStringRooter.str");
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
case IDVECTOR: {
|
|
|
|
Vector<jsid, 8> &vector = static_cast<js::AutoIdVector *>(this)->vector;
|
|
|
|
MarkIdRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector");
|
2010-06-16 14:13:01 -07:00
|
|
|
return;
|
|
|
|
}
|
2011-01-05 14:50:30 -08:00
|
|
|
|
2011-02-07 12:06:32 -08:00
|
|
|
case SHAPEVECTOR: {
|
|
|
|
Vector<const Shape *, 8> &vector = static_cast<js::AutoShapeVector *>(this)->vector;
|
|
|
|
MarkShapeRange(trc, vector.length(), vector.begin(), "js::AutoShapeVector.vector");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-01-05 14:50:30 -08:00
|
|
|
case BINDINGS: {
|
|
|
|
static_cast<js::AutoBindingsRooter *>(this)->bindings.trace(trc);
|
|
|
|
return;
|
|
|
|
}
|
2010-06-16 14:13:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JS_ASSERT(tag >= 0);
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkValueRange(trc, tag, static_cast<AutoArrayRooter *>(this)->array, "js::AutoArrayRooter.array");
|
2010-06-16 14:13:01 -07:00
|
|
|
}
|
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
namespace js {
|
|
|
|
|
2011-01-08 18:55:54 -08:00
|
|
|
JS_FRIEND_API(void)
|
2010-08-30 11:46:18 -07:00
|
|
|
MarkContext(JSTracer *trc, JSContext *acx)
|
2007-04-16 23:53:37 -07:00
|
|
|
{
|
2010-03-03 17:52:26 -08:00
|
|
|
/* Stack frames and slots are traced by StackSpace::mark. */
|
2007-04-16 23:53:37 -07:00
|
|
|
|
|
|
|
/* Mark other roots-by-definition in acx. */
|
2011-01-27 02:54:58 -08:00
|
|
|
if (acx->globalObject && !acx->hasRunOption(JSOPTION_UNROOTED_GLOBAL))
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkObject(trc, *acx->globalObject, "global object");
|
2011-01-07 02:03:14 -08:00
|
|
|
if (acx->isExceptionPending())
|
|
|
|
MarkValue(trc, acx->getPendingException(), "exception");
|
2007-04-16 23:53:37 -07:00
|
|
|
|
2010-03-28 13:34:16 -07:00
|
|
|
for (js::AutoGCRooter *gcr = acx->autoGCRooters; gcr; gcr = gcr->down)
|
|
|
|
gcr->trace(trc);
|
|
|
|
|
2007-04-16 23:53:37 -07:00
|
|
|
if (acx->sharpObjectMap.depth > 0)
|
|
|
|
js_TraceSharpMap(trc, &acx->sharpObjectMap);
|
2008-12-18 12:06:45 -08:00
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
MarkValue(trc, acx->iterValue, "iterValue");
|
2007-04-16 23:53:37 -07:00
|
|
|
}
|
|
|
|
|
2009-01-30 15:40:05 -08:00
|
|
|
JS_REQUIRES_STACK void
|
2010-08-30 11:46:18 -07:00
|
|
|
MarkRuntime(JSTracer *trc)
|
2007-04-16 23:53:37 -07:00
|
|
|
{
|
|
|
|
JSRuntime *rt = trc->context->runtime;
|
|
|
|
|
2010-07-26 01:24:27 -07:00
|
|
|
if (rt->state != JSRTS_LANDING)
|
2010-08-05 05:16:56 -07:00
|
|
|
MarkConservativeStackRoots(trc);
|
2010-07-26 01:24:27 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify that we do not have at this point unmarked GC things stored in
|
|
|
|
* autorooters. To maximize test coverage we abort even in non-debug
|
|
|
|
* builds for now, see bug 574313.
|
|
|
|
*/
|
|
|
|
JSContext *iter;
|
2011-01-07 23:44:57 -08:00
|
|
|
#if 0
|
2010-07-26 01:24:27 -07:00
|
|
|
iter = NULL;
|
|
|
|
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) {
|
|
|
|
for (AutoGCRooter *gcr = acx->autoGCRooters; gcr; gcr = gcr->down) {
|
|
|
|
#ifdef JS_THREADSAFE
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT_IF(!acx->thread->data.requestDepth, acx->thread->suspendCount);
|
2010-07-26 01:24:27 -07:00
|
|
|
#endif
|
2010-08-30 11:46:18 -07:00
|
|
|
JS_ASSERT(JS_THREAD_DATA(acx)->conservativeGC.hasStackToScan());
|
2010-07-26 01:24:27 -07:00
|
|
|
void *thing;
|
|
|
|
switch (gcr->tag) {
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
case AutoGCRooter::JSVAL: {
|
|
|
|
const Value &v = static_cast<AutoValueRooter *>(gcr)->val;
|
|
|
|
if (!v.isMarkable())
|
|
|
|
continue;
|
2010-09-24 10:54:39 -07:00
|
|
|
thing = v.toGCThing();
|
2010-07-26 01:24:27 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AutoGCRooter::XML:
|
|
|
|
thing = static_cast<AutoXMLRooter *>(gcr)->xml;
|
|
|
|
break;
|
|
|
|
case AutoGCRooter::OBJECT:
|
|
|
|
thing = static_cast<AutoObjectRooter *>(gcr)->obj;
|
|
|
|
if (!thing)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case AutoGCRooter::ID: {
|
|
|
|
jsid id = static_cast<AutoIdRooter *>(gcr)->id();
|
|
|
|
if (!JSID_IS_GCTHING(id))
|
|
|
|
continue;
|
|
|
|
thing = JSID_TO_GCTHING(id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-14 13:55:55 -07:00
|
|
|
if (JSString::isGCThingStatic(thing))
|
2010-07-26 01:24:27 -07:00
|
|
|
continue;
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
if (!reinterpret_cast<Cell *>(thing)->isMarked()) {
|
|
|
|
ConservativeGCTest test = MarkIfGCThingWord(trc, reinterpret_cast<jsuword>(thing));
|
2010-07-26 01:24:27 -07:00
|
|
|
fprintf(stderr,
|
2010-08-06 12:45:25 -07:00
|
|
|
"Conservative GC scanner has missed the root 0x%p with tag %ld"
|
|
|
|
" on the stack due to %d. The root location 0x%p, distance from"
|
|
|
|
" the stack base %ld, conservative gc span %ld."
|
|
|
|
" Consevtaive GC status for the thread %d."
|
|
|
|
" Aborting.\n",
|
|
|
|
thing, (long) gcr->tag, int(test), (void *) gcr,
|
|
|
|
(long) ((jsword) JS_THREAD_DATA(acx)->nativeStackBase - (jsword) gcr),
|
|
|
|
(long) ((jsword) JS_THREAD_DATA(acx)->nativeStackBase -
|
|
|
|
(jsword) JS_THREAD_DATA(acx)->conservativeGC.nativeStackTop),
|
2010-08-30 11:46:18 -07:00
|
|
|
int(JS_THREAD_DATA(acx)->conservativeGC.hasStackToScan()));
|
2010-07-26 01:24:27 -07:00
|
|
|
JS_ASSERT(false);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
|
|
|
|
gc_root_traversal(trc, r.front());
|
2010-05-20 13:50:08 -07:00
|
|
|
|
|
|
|
for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront())
|
|
|
|
gc_lock_traversal(r.front(), trc);
|
|
|
|
|
2010-04-23 15:15:42 -07:00
|
|
|
js_TraceAtomState(trc);
|
2009-11-12 14:13:25 -08:00
|
|
|
js_MarkTraps(trc);
|
2007-04-16 23:53:37 -07:00
|
|
|
|
2010-07-26 01:24:27 -07:00
|
|
|
iter = NULL;
|
2010-06-04 07:22:28 -07:00
|
|
|
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
|
2010-08-30 11:46:18 -07:00
|
|
|
MarkContext(trc, acx);
|
2007-05-01 03:09:46 -07:00
|
|
|
|
2011-01-12 16:56:23 -08:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
|
|
|
(*c)->traceMonitor.mark(trc);
|
|
|
|
#endif
|
|
|
|
|
2010-05-13 10:50:43 -07:00
|
|
|
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
|
|
|
|
i.threadData()->mark(trc);
|
2009-08-17 14:50:57 -07:00
|
|
|
|
2010-06-04 07:22:28 -07:00
|
|
|
/*
|
2010-07-26 01:24:27 -07:00
|
|
|
* We mark extra roots at the last thing so it can use use additional
|
|
|
|
* colors to implement cycle collection.
|
2010-06-04 07:22:28 -07:00
|
|
|
*/
|
2010-07-15 17:58:36 -07:00
|
|
|
if (rt->gcExtraRootsTraceOp)
|
|
|
|
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
|
2010-08-01 09:58:03 -07:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (rt->functionMeterFilename) {
|
|
|
|
for (int k = 0; k < 2; k++) {
|
|
|
|
typedef JSRuntime::FunctionCountMap HM;
|
|
|
|
HM &h = (k == 0) ? rt->methodReadBarrierCountMap : rt->unjoinedFunctionCountMap;
|
|
|
|
for (HM::Range r = h.all(); !r.empty(); r.popFront()) {
|
|
|
|
JSFunction *fun = r.front().key;
|
|
|
|
JS_CALL_OBJECT_TRACER(trc, fun, "FunctionCountMap key");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2007-04-16 23:53:37 -07:00
|
|
|
}
|
|
|
|
|
2010-07-28 11:20:19 -07:00
|
|
|
void
|
2010-09-07 14:08:20 -07:00
|
|
|
TriggerGC(JSRuntime *rt)
|
2010-07-28 11:20:19 -07:00
|
|
|
{
|
|
|
|
JS_ASSERT(!rt->gcRunning);
|
|
|
|
if (rt->gcIsNeeded)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trigger the GC when it is safe to call an operation callback on any
|
|
|
|
* thread.
|
|
|
|
*/
|
2010-09-07 14:08:20 -07:00
|
|
|
rt->gcIsNeeded = true;
|
2011-01-07 23:44:57 -08:00
|
|
|
rt->gcTriggerCompartment = NULL;
|
2010-09-07 14:08:20 -07:00
|
|
|
TriggerAllOperationCallbacks(rt);
|
2010-07-28 11:20:19 -07:00
|
|
|
}
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
void
|
|
|
|
TriggerCompartmentGC(JSCompartment *comp)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = comp->rt;
|
|
|
|
JS_ASSERT(!rt->gcRunning);
|
|
|
|
|
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
if (rt->gcZeal >= 1) {
|
|
|
|
TriggerGC(rt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-01-13 14:42:36 -08:00
|
|
|
if (rt->gcMode != JSGC_MODE_COMPARTMENT || comp == rt->atomsCompartment) {
|
2011-01-07 23:44:57 -08:00
|
|
|
/* We can't do a compartmental GC of the default compartment. */
|
|
|
|
TriggerGC(rt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rt->gcIsNeeded) {
|
|
|
|
/* If we need to GC more than one compartment, run a full GC. */
|
|
|
|
if (rt->gcTriggerCompartment != comp)
|
|
|
|
rt->gcTriggerCompartment = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rt->gcBytes > 8192 && rt->gcBytes >= 3 * (rt->gcTriggerBytes / 2)) {
|
|
|
|
/* If we're using significantly more than our quota, do a full GC. */
|
|
|
|
TriggerGC(rt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trigger the GC when it is safe to call an operation callback on any
|
|
|
|
* thread.
|
|
|
|
*/
|
|
|
|
rt->gcIsNeeded = true;
|
|
|
|
rt->gcTriggerCompartment = comp;
|
|
|
|
TriggerAllOperationCallbacks(comp->rt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MaybeGC(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
|
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
if (rt->gcZeal > 0) {
|
|
|
|
js_GC(cx, NULL, GC_NORMAL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
JSCompartment *comp = cx->compartment;
|
|
|
|
if (rt->gcIsNeeded) {
|
2011-02-07 11:24:08 -08:00
|
|
|
js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL);
|
2011-01-07 23:44:57 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4))
|
2011-02-07 11:24:08 -08:00
|
|
|
js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL);
|
2011-01-07 23:44:57 -08:00
|
|
|
}
|
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
} /* namespace js */
|
|
|
|
|
2009-03-24 05:07:35 -07:00
|
|
|
void
|
2010-12-22 12:02:25 -08:00
|
|
|
js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp)
|
2008-08-20 22:18:42 -07:00
|
|
|
{
|
2009-03-24 05:07:35 -07:00
|
|
|
JSScript **listp, *script;
|
2008-08-20 22:18:42 -07:00
|
|
|
|
2010-12-22 12:02:25 -08:00
|
|
|
for (size_t i = 0; i != JS_ARRAY_LENGTH(comp->scriptsToGC); ++i) {
|
|
|
|
listp = &comp->scriptsToGC[i];
|
2009-03-24 05:07:35 -07:00
|
|
|
while ((script = *listp) != NULL) {
|
|
|
|
*listp = script->u.nextToGC;
|
|
|
|
script->u.nextToGC = NULL;
|
2011-02-11 13:23:18 -08:00
|
|
|
js_DestroyCachedScript(cx, script);
|
2009-03-24 05:07:35 -07:00
|
|
|
}
|
2008-08-20 22:18:42 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-30 03:06:26 -08:00
|
|
|
/*
|
|
|
|
* This function is called from js_FinishAtomState to force the finalization
|
|
|
|
* of the permanently interned strings when cx is not available.
|
|
|
|
*/
|
2009-10-02 07:34:22 -07:00
|
|
|
void
|
|
|
|
js_FinalizeStringRT(JSRuntime *rt, JSString *str)
|
|
|
|
{
|
2009-12-30 03:06:26 -08:00
|
|
|
JS_RUNTIME_UNMETER(rt, liveStrings);
|
2011-03-14 13:59:53 -07:00
|
|
|
JS_ASSERT(str->isLinear() && !str->isStaticAtom());
|
2009-10-02 07:34:22 -07:00
|
|
|
|
2009-12-30 03:06:26 -08:00
|
|
|
if (str->isDependent()) {
|
|
|
|
/* A dependent string can not be external and must be valid. */
|
2011-03-14 13:59:53 -07:00
|
|
|
JS_ASSERT(str->arena()->header()->thingKind == FINALIZE_STRING);
|
|
|
|
JS_ASSERT(str->asDependent().base());
|
2009-12-30 03:06:26 -08:00
|
|
|
JS_RUNTIME_UNMETER(rt, liveDependentStrings);
|
|
|
|
} else {
|
2011-03-14 13:59:53 -07:00
|
|
|
unsigned thingKind = str->arena()->header()->thingKind;
|
2011-03-14 13:55:43 -07:00
|
|
|
JS_ASSERT(unsigned(FINALIZE_SHORT_STRING) <= thingKind &&
|
|
|
|
thingKind <= unsigned(FINALIZE_EXTERNAL_STRING));
|
2010-01-14 00:27:32 -08:00
|
|
|
|
2011-03-14 13:59:53 -07:00
|
|
|
jschar *chars = const_cast<jschar *>(str->asFlat().chars());
|
2009-12-30 03:06:26 -08:00
|
|
|
if (thingKind == FINALIZE_STRING) {
|
2011-01-07 03:03:34 -08:00
|
|
|
rt->stringMemoryUsed -= str->length() * 2;
|
2009-12-30 03:06:26 -08:00
|
|
|
rt->free(chars);
|
2010-11-15 12:39:00 -08:00
|
|
|
} else if (thingKind == FINALIZE_EXTERNAL_STRING) {
|
|
|
|
((JSExternalString *)str)->finalize();
|
2009-12-30 03:06:26 -08:00
|
|
|
}
|
|
|
|
}
|
2009-10-02 07:34:22 -07:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
template<typename T>
|
2009-10-02 07:34:22 -07:00
|
|
|
static void
|
2010-09-24 10:54:39 -07:00
|
|
|
FinalizeArenaList(JSCompartment *comp, JSContext *cx, unsigned thingKind)
|
2009-10-02 07:34:22 -07:00
|
|
|
{
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_STATIC_ASSERT(!(sizeof(T) & Cell::CellMask));
|
2010-10-13 11:49:22 -07:00
|
|
|
ArenaList *arenaList = GetFinalizableArenaList(comp, thingKind);
|
|
|
|
Arena<FreeCell> **ap = &arenaList->head;
|
|
|
|
Arena<T> *a = (Arena<T> *) *ap;
|
2009-10-02 07:34:22 -07:00
|
|
|
if (!a)
|
|
|
|
return;
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(sizeof(T) == arenaList->head->header()->thingSize);
|
2009-10-02 07:34:22 -07:00
|
|
|
|
|
|
|
#ifdef JS_GCMETER
|
|
|
|
uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0;
|
|
|
|
#endif
|
|
|
|
for (;;) {
|
2010-10-13 11:49:22 -07:00
|
|
|
ArenaHeader *header = a->header();
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT_IF(header->hasFreeThings, header->freeList);
|
|
|
|
JS_ASSERT(header->thingKind == thingKind);
|
2010-04-12 13:59:19 -07:00
|
|
|
JS_ASSERT(!a->getMarkingDelay()->link);
|
|
|
|
JS_ASSERT(a->getMarkingDelay()->unmarkedChildren == 0);
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(a->header()->isUsed);
|
2009-10-02 07:34:22 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
FreeCell *nextFree = header->freeList;
|
|
|
|
FreeCell *freeList = NULL;
|
|
|
|
FreeCell **tailp = &freeList;
|
2009-10-02 07:34:22 -07:00
|
|
|
bool allClear = true;
|
2010-01-14 00:27:32 -08:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
T *thingsEnd = &a->t.things[a->ThingsPerArena-1].t;
|
|
|
|
T *thing = &a->t.things[0].t;
|
|
|
|
thingsEnd++;
|
2010-01-14 00:27:32 -08:00
|
|
|
|
|
|
|
if (!nextFree) {
|
2010-09-24 10:54:39 -07:00
|
|
|
nextFree = thingsEnd->asFreeCell();
|
2010-01-14 00:27:32 -08:00
|
|
|
} else {
|
2011-03-14 13:59:53 -07:00
|
|
|
JS_ASSERT(thing->asFreeCell() <= nextFree);
|
|
|
|
JS_ASSERT(nextFree < thingsEnd->asFreeCell());
|
2010-01-14 00:27:32 -08:00
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
for (;; thing++) {
|
2011-03-14 13:59:53 -07:00
|
|
|
if (thing->asFreeCell() == nextFree) {
|
2009-10-15 23:10:54 -07:00
|
|
|
if (thing == thingsEnd)
|
|
|
|
break;
|
2010-09-24 10:54:39 -07:00
|
|
|
nextFree = nextFree->link;
|
2009-10-15 23:10:54 -07:00
|
|
|
if (!nextFree) {
|
2010-09-24 10:54:39 -07:00
|
|
|
nextFree = thingsEnd->asFreeCell();
|
2009-10-15 23:10:54 -07:00
|
|
|
} else {
|
2011-03-14 13:59:53 -07:00
|
|
|
JS_ASSERT(thing->asFreeCell() < nextFree);
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(nextFree < thingsEnd->asFreeCell());
|
2009-10-15 23:10:54 -07:00
|
|
|
}
|
2011-03-14 13:59:53 -07:00
|
|
|
} else if (thing->isMarked()) {
|
2010-01-14 00:27:32 -08:00
|
|
|
allClear = false;
|
|
|
|
METER(nthings++);
|
|
|
|
continue;
|
2009-10-02 07:34:22 -07:00
|
|
|
} else {
|
2010-11-15 12:39:00 -08:00
|
|
|
thing->finalize(cx);
|
2009-10-02 07:34:22 -07:00
|
|
|
#ifdef DEBUG
|
2010-09-24 10:54:39 -07:00
|
|
|
memset(thing, JS_FREE_PATTERN, sizeof(T));
|
2009-10-02 07:34:22 -07:00
|
|
|
#endif
|
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
FreeCell *t = thing->asFreeCell();
|
2010-04-12 13:59:19 -07:00
|
|
|
*tailp = t;
|
|
|
|
tailp = &t->link;
|
2009-10-15 23:10:54 -07:00
|
|
|
}
|
2009-10-02 07:34:22 -07:00
|
|
|
|
2009-10-15 23:10:54 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
/* Check that the free list is consistent. */
|
|
|
|
unsigned nfree = 0;
|
|
|
|
if (freeList) {
|
|
|
|
JS_ASSERT(tailp != &freeList);
|
2010-09-24 10:54:39 -07:00
|
|
|
FreeCell *t = freeList;
|
2009-10-15 23:10:54 -07:00
|
|
|
for (;;) {
|
|
|
|
++nfree;
|
2010-04-12 13:59:19 -07:00
|
|
|
if (&t->link == tailp)
|
2009-10-15 23:10:54 -07:00
|
|
|
break;
|
2010-04-12 13:59:19 -07:00
|
|
|
JS_ASSERT(t < t->link);
|
|
|
|
t = t->link;
|
2009-10-15 23:10:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2009-10-02 07:34:22 -07:00
|
|
|
if (allClear) {
|
|
|
|
/*
|
|
|
|
* Forget just assembled free list head for the arena and
|
|
|
|
* add the arena itself to the destroy list.
|
|
|
|
*/
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(nfree == a->ThingsPerArena);
|
|
|
|
JS_ASSERT((T *)tailp == &a->t.things[a->ThingsPerArena-1].t);
|
|
|
|
*tailp = NULL;
|
|
|
|
header->freeList = freeList;
|
|
|
|
#ifdef DEBUG
|
|
|
|
header->hasFreeThings = true;
|
|
|
|
#endif
|
|
|
|
*ap = (header->next);
|
|
|
|
JS_ASSERT((T *)header->freeList == &a->t.things[0].t);
|
2010-10-13 11:49:22 -07:00
|
|
|
a->chunk()->releaseArena(a);
|
2009-10-02 07:34:22 -07:00
|
|
|
METER(nkilledarenas++);
|
|
|
|
} else {
|
2010-09-24 10:54:39 -07:00
|
|
|
JS_ASSERT(nfree < a->ThingsPerArena);
|
2009-10-15 23:10:54 -07:00
|
|
|
*tailp = NULL;
|
2010-09-24 10:54:39 -07:00
|
|
|
header->freeList = freeList;
|
|
|
|
#ifdef DEBUG
|
|
|
|
header->hasFreeThings = (nfree == 0) ? false : true;
|
|
|
|
#endif
|
|
|
|
ap = &header->next;
|
2009-10-02 07:34:22 -07:00
|
|
|
METER(nlivearenas++);
|
|
|
|
}
|
2010-10-13 11:49:22 -07:00
|
|
|
if (!(a = (Arena<T> *) *ap))
|
2009-10-02 07:34:22 -07:00
|
|
|
break;
|
|
|
|
}
|
2009-10-15 23:10:54 -07:00
|
|
|
arenaList->cursor = arenaList->head;
|
2010-09-24 10:54:39 -07:00
|
|
|
METER(UpdateCompartmentStats(comp, thingKind, nlivearenas, nkilledarenas, nthings));
|
2009-10-02 07:34:22 -07:00
|
|
|
}
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
void
|
|
|
|
JSCompartment::finalizeObjectArenaLists(JSContext *cx)
|
|
|
|
{
|
|
|
|
FinalizeArenaList<JSObject>(this, cx, FINALIZE_OBJECT0);
|
|
|
|
FinalizeArenaList<JSObject_Slots2>(this, cx, FINALIZE_OBJECT2);
|
|
|
|
FinalizeArenaList<JSObject_Slots4>(this, cx, FINALIZE_OBJECT4);
|
|
|
|
FinalizeArenaList<JSObject_Slots8>(this, cx, FINALIZE_OBJECT8);
|
|
|
|
FinalizeArenaList<JSObject_Slots12>(this, cx, FINALIZE_OBJECT12);
|
|
|
|
FinalizeArenaList<JSObject_Slots16>(this, cx, FINALIZE_OBJECT16);
|
|
|
|
FinalizeArenaList<JSFunction>(this, cx, FINALIZE_FUNCTION);
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
FinalizeArenaList<JSXML>(this, cx, FINALIZE_XML);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
JSCompartment::finalizeStringArenaLists(JSContext *cx)
|
|
|
|
{
|
|
|
|
FinalizeArenaList<JSShortString>(this, cx, FINALIZE_SHORT_STRING);
|
|
|
|
FinalizeArenaList<JSString>(this, cx, FINALIZE_STRING);
|
|
|
|
FinalizeArenaList<JSExternalString>(this, cx, FINALIZE_EXTERNAL_STRING);
|
|
|
|
}
|
|
|
|
|
2010-04-27 06:46:24 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
|
|
|
|
namespace js {
|
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
bool
|
|
|
|
GCHelperThread::init(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
if (!(wakeup = PR_NewCondVar(rt->gcLock)))
|
|
|
|
return false;
|
|
|
|
if (!(sweepingDone = PR_NewCondVar(rt->gcLock)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
thread = PR_CreateThread(PR_USER_THREAD, threadMain, rt, PR_PRIORITY_NORMAL,
|
|
|
|
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
|
|
|
|
return !!thread;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GCHelperThread::finish(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
PRThread *join = NULL;
|
|
|
|
{
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
if (thread && !shutdown) {
|
|
|
|
shutdown = true;
|
|
|
|
PR_NotifyCondVar(wakeup);
|
|
|
|
join = thread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (join) {
|
|
|
|
/* PR_DestroyThread is not necessary. */
|
|
|
|
PR_JoinThread(join);
|
|
|
|
}
|
|
|
|
if (wakeup)
|
|
|
|
PR_DestroyCondVar(wakeup);
|
|
|
|
if (sweepingDone)
|
|
|
|
PR_DestroyCondVar(sweepingDone);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
void
|
|
|
|
GCHelperThread::threadMain(void *arg)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = static_cast<JSRuntime *>(arg);
|
|
|
|
rt->gcHelperThread.threadLoop(rt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GCHelperThread::threadLoop(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
while (!shutdown) {
|
|
|
|
/*
|
|
|
|
* Sweeping can be true here on the first iteration if a GC and the
|
|
|
|
* corresponding startBackgroundSweep call happen before this thread
|
|
|
|
* has a chance to run.
|
|
|
|
*/
|
|
|
|
if (!sweeping)
|
|
|
|
PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
if (sweeping) {
|
|
|
|
AutoUnlockGC unlock(rt);
|
|
|
|
doSweep();
|
|
|
|
}
|
|
|
|
sweeping = false;
|
|
|
|
PR_NotifyAllCondVar(sweepingDone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GCHelperThread::startBackgroundSweep(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
/* The caller takes the GC lock. */
|
|
|
|
JS_ASSERT(!sweeping);
|
|
|
|
sweeping = true;
|
|
|
|
PR_NotifyCondVar(wakeup);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
while (sweeping)
|
|
|
|
PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2010-04-28 17:17:34 -07:00
|
|
|
JS_FRIEND_API(void)
|
2010-09-07 14:08:20 -07:00
|
|
|
GCHelperThread::replenishAndFreeLater(void *ptr)
|
2010-04-27 06:46:24 -07:00
|
|
|
{
|
|
|
|
JS_ASSERT(freeCursor == freeCursorEnd);
|
|
|
|
do {
|
|
|
|
if (freeCursor && !freeVector.append(freeCursorEnd - FREE_ARRAY_LENGTH))
|
|
|
|
break;
|
|
|
|
freeCursor = (void **) js_malloc(FREE_ARRAY_SIZE);
|
|
|
|
if (!freeCursor) {
|
|
|
|
freeCursorEnd = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
freeCursorEnd = freeCursor + FREE_ARRAY_LENGTH;
|
|
|
|
*freeCursor++ = ptr;
|
|
|
|
return;
|
|
|
|
} while (false);
|
|
|
|
js_free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-09-07 14:08:20 -07:00
|
|
|
GCHelperThread::doSweep()
|
2010-04-27 06:46:24 -07:00
|
|
|
{
|
|
|
|
if (freeCursor) {
|
|
|
|
void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
|
|
|
|
freeElementsAndArray(array, freeCursor);
|
|
|
|
freeCursor = freeCursorEnd = NULL;
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(!freeCursorEnd);
|
|
|
|
}
|
|
|
|
for (void ***iter = freeVector.begin(); iter != freeVector.end(); ++iter) {
|
|
|
|
void **array = *iter;
|
|
|
|
freeElementsAndArray(array, array + FREE_ARRAY_LENGTH);
|
|
|
|
}
|
2010-09-07 14:08:20 -07:00
|
|
|
freeVector.resize(0);
|
2010-04-27 06:46:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* JS_THREADSAFE */
|
|
|
|
|
2010-06-23 14:35:10 -07:00
|
|
|
static void
|
2011-01-08 20:06:29 -08:00
|
|
|
SweepCrossCompartmentWrappers(JSContext *cx)
|
2010-06-23 14:35:10 -07:00
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
2010-12-17 16:33:04 -08:00
|
|
|
/*
|
|
|
|
* Figure out how much JIT code should be released from inactive compartments.
|
2011-01-18 15:20:39 -08:00
|
|
|
* If multiple eighth-lives have passed, compound the release interval linearly;
|
2010-12-17 16:33:04 -08:00
|
|
|
* if enough time has passed, all inactive JIT code will be released.
|
|
|
|
*/
|
|
|
|
uint32 releaseInterval = 0;
|
|
|
|
int64 now = PRMJ_Now();
|
|
|
|
if (now >= rt->gcJitReleaseTime) {
|
|
|
|
releaseInterval = 8;
|
|
|
|
while (now >= rt->gcJitReleaseTime) {
|
|
|
|
if (--releaseInterval == 1)
|
|
|
|
rt->gcJitReleaseTime = now;
|
|
|
|
rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-08 20:06:29 -08:00
|
|
|
/* Remove dead wrappers from the compartment map. */
|
2011-01-18 15:20:39 -08:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
2011-01-08 20:06:29 -08:00
|
|
|
(*c)->sweep(cx, releaseInterval);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
JSCompartmentCallback callback = rt->compartmentCallback;
|
2011-01-18 15:20:39 -08:00
|
|
|
|
|
|
|
/* Skip the atomsCompartment. */
|
|
|
|
JSCompartment **read = rt->compartments.begin() + 1;
|
2011-01-08 20:06:29 -08:00
|
|
|
JSCompartment **end = rt->compartments.end();
|
|
|
|
JSCompartment **write = read;
|
2011-01-18 15:20:39 -08:00
|
|
|
JS_ASSERT(rt->compartments.length() >= 1);
|
|
|
|
JS_ASSERT(*rt->compartments.begin() == rt->atomsCompartment);
|
2011-01-13 19:04:03 -08:00
|
|
|
|
2010-06-23 14:35:10 -07:00
|
|
|
while (read < end) {
|
2011-01-18 15:20:39 -08:00
|
|
|
JSCompartment *compartment = *read++;
|
|
|
|
|
2011-03-08 20:58:38 -08:00
|
|
|
if (!compartment->hold &&
|
|
|
|
(compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) {
|
2011-01-13 19:04:03 -08:00
|
|
|
JS_ASSERT(compartment->freeLists.isEmpty());
|
2011-01-18 15:20:39 -08:00
|
|
|
if (callback)
|
|
|
|
(void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
|
|
|
|
if (compartment->principals)
|
|
|
|
JSPRINCIPALS_DROP(cx, compartment->principals);
|
|
|
|
js_delete(compartment);
|
|
|
|
continue;
|
2010-06-23 14:35:10 -07:00
|
|
|
}
|
2011-01-18 15:20:39 -08:00
|
|
|
*write++ = compartment;
|
2010-06-23 14:35:10 -07:00
|
|
|
}
|
|
|
|
rt->compartments.resize(write - rt->compartments.begin());
|
|
|
|
}
|
|
|
|
|
2010-04-08 05:54:18 -07:00
|
|
|
/*
|
|
|
|
* Common cache invalidation and so forth that must be done before GC. Even if
|
2010-08-29 11:57:08 -07:00
|
|
|
* GCUntilDone calls GC several times, this work needs to be done only once.
|
2010-04-08 05:54:18 -07:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PreGCCleanup(JSContext *cx, JSGCInvocationKind gckind)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
|
|
|
|
/* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
|
2011-01-07 23:44:57 -08:00
|
|
|
rt->gcIsNeeded = false;
|
|
|
|
rt->gcTriggerCompartment = NULL;
|
2010-04-08 05:54:18 -07:00
|
|
|
|
|
|
|
/* Reset malloc counter. */
|
|
|
|
rt->resetGCMallocBytes();
|
|
|
|
|
|
|
|
#ifdef JS_DUMP_SCOPE_METERS
|
|
|
|
{
|
|
|
|
extern void js_DumpScopeMeters(JSRuntime *rt);
|
|
|
|
js_DumpScopeMeters(rt);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the property cache's type id generator so we can compress ids.
|
|
|
|
* Same for the protoHazardShape proxy-shape standing in for all object
|
|
|
|
* prototypes having readonly or setter properties.
|
|
|
|
*/
|
|
|
|
if (rt->shapeGen & SHAPE_OVERFLOW_BIT
|
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
|| rt->gcZeal >= 1
|
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
rt->gcRegenShapes = true;
|
2011-02-04 10:59:07 -08:00
|
|
|
rt->shapeGen = 0;
|
2010-04-08 05:54:18 -07:00
|
|
|
rt->protoHazardShape = 0;
|
|
|
|
}
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
if (rt->gcCurrentCompartment) {
|
|
|
|
rt->gcCurrentCompartment->purge(cx);
|
|
|
|
} else {
|
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
|
|
|
(*c)->purge(cx);
|
|
|
|
}
|
2010-04-08 05:54:18 -07:00
|
|
|
|
|
|
|
js_PurgeThreads(cx);
|
|
|
|
{
|
|
|
|
JSContext *iter = NULL;
|
|
|
|
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
|
|
|
|
acx->purge();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
static void
|
|
|
|
MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_PARAM)
|
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
rt->gcNumber++;
|
|
|
|
JS_ASSERT(!rt->gcRegenShapes);
|
|
|
|
JS_ASSERT(gckind != GC_LAST_CONTEXT);
|
2011-02-04 10:59:07 -08:00
|
|
|
JS_ASSERT(comp != rt->atomsCompartment);
|
2011-02-07 11:24:08 -08:00
|
|
|
JS_ASSERT(comp->rt->gcMode == JSGC_MODE_COMPARTMENT);
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark phase.
|
|
|
|
*/
|
|
|
|
GCMarker gcmarker(cx);
|
|
|
|
JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker));
|
|
|
|
JS_ASSERT(gcmarker.getMarkColor() == BLACK);
|
|
|
|
rt->gcMarkingTracer = &gcmarker;
|
|
|
|
gcmarker.stackLimit = cx->stackLimit;
|
|
|
|
|
|
|
|
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
|
|
|
r.front()->clearMarkBitmap();
|
|
|
|
|
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
2011-03-08 20:58:38 -08:00
|
|
|
(*c)->markCrossCompartmentWrappers(&gcmarker);
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
MarkRuntime(&gcmarker);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark children of things that caused too deep recursion during the above
|
|
|
|
* tracing.
|
|
|
|
*/
|
|
|
|
gcmarker.markDelayedChildren();
|
|
|
|
|
2011-03-07 16:56:17 -08:00
|
|
|
/*
|
|
|
|
* Mark weak roots.
|
|
|
|
*/
|
|
|
|
while (true) {
|
|
|
|
if (!js_TraceWatchPoints(&gcmarker))
|
|
|
|
break;
|
|
|
|
gcmarker.markDelayedChildren();
|
|
|
|
}
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
rt->gcMarkingTracer = NULL;
|
|
|
|
|
|
|
|
if (rt->gcCallback)
|
|
|
|
(void) rt->gcCallback(cx, JSGC_MARK_END);
|
|
|
|
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/*
|
|
|
|
* cx->gcBackgroundFree is set if we need several mark-and-sweep loops to
|
|
|
|
* finish the GC.
|
|
|
|
*/
|
|
|
|
if(!cx->gcBackgroundFree) {
|
|
|
|
/* Wait until the sweeping from the previois GC finishes. */
|
|
|
|
rt->gcHelperThread.waitBackgroundSweepEnd(rt);
|
|
|
|
cx->gcBackgroundFree = &rt->gcHelperThread;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* Make sure that we didn't mark an object in another compartment */
|
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
2011-03-14 13:55:58 -07:00
|
|
|
JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, checkArenaListAllUnmarked(*c));
|
2011-01-07 23:44:57 -08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sweep phase.
|
|
|
|
*
|
|
|
|
* Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
|
|
|
|
* so that any attempt to allocate a GC-thing from a finalizer will fail,
|
|
|
|
* rather than nest badly and leave the unmarked newborn to be swept.
|
|
|
|
*
|
2011-03-14 13:55:55 -07:00
|
|
|
* We first sweep atom state so we can use IsAboutToBeFinalized on
|
2011-01-07 23:44:57 -08:00
|
|
|
* JSString held in a hashtable to check if the hashtable entry can be
|
|
|
|
* freed. Note that even after the entry is freed, JSObject finalizers can
|
|
|
|
* continue to access the corresponding JSString* assuming that they are
|
|
|
|
* unique. This works since the atomization API must not be called during
|
|
|
|
* the GC.
|
|
|
|
*/
|
|
|
|
TIMESTAMP(startSweep);
|
|
|
|
js_SweepAtomState(cx);
|
|
|
|
|
|
|
|
/* Finalize watch points associated with unreachable objects. */
|
|
|
|
js_SweepWatchPoints(cx);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* Save the pre-sweep count of scope-mapped properties. */
|
|
|
|
rt->liveObjectPropsPreSweep = rt->liveObjectProps;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We finalize iterators before other objects so the iterator can use the
|
|
|
|
* object which properties it enumerates over to finalize the enumeration
|
|
|
|
* state. We finalize objects before other GC things to ensure that
|
|
|
|
* object's finalizer can access them even if they will be freed.
|
|
|
|
*/
|
2011-01-08 20:06:29 -08:00
|
|
|
comp->sweep(cx, 0);
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
comp->finalizeObjectArenaLists(cx);
|
|
|
|
TIMESTAMP(sweepObjectEnd);
|
|
|
|
|
|
|
|
comp->finalizeStringArenaLists(cx);
|
|
|
|
TIMESTAMP(sweepStringEnd);
|
|
|
|
|
2011-02-22 12:45:18 -08:00
|
|
|
/*
|
|
|
|
* Unmark all shapes. Even a per-compartment GC can mark shapes in other
|
|
|
|
* compartments, and we need to clear these bits. See bug 635873. This will
|
|
|
|
* be fixed in bug 569422.
|
|
|
|
*/
|
2011-02-04 10:59:07 -08:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
2011-02-22 12:45:18 -08:00
|
|
|
(*c)->propertyTree.unmarkShapes(cx);
|
2011-02-04 10:59:07 -08:00
|
|
|
|
|
|
|
PropertyTree::dumpShapes(cx);
|
2011-02-23 19:58:19 -08:00
|
|
|
TIMESTAMP(sweepShapeEnd);
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy arenas after we finished the sweeping so finalizers can safely
|
2011-03-14 13:55:55 -07:00
|
|
|
* use IsAboutToBeFinalized().
|
2011-01-07 23:44:57 -08:00
|
|
|
*/
|
|
|
|
ExpireGCChunks(rt);
|
|
|
|
TIMESTAMP(sweepDestroyEnd);
|
|
|
|
|
|
|
|
if (rt->gcCallback)
|
|
|
|
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
|
|
|
|
}
|
|
|
|
|
2010-04-08 05:54:17 -07:00
|
|
|
/*
|
|
|
|
* Perform mark-and-sweep GC.
|
|
|
|
*
|
|
|
|
* In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each
|
|
|
|
* other thread must be either outside all requests or blocked waiting for GC
|
|
|
|
* to finish. Note that the caller does not hold rt->gcLock.
|
|
|
|
*/
|
|
|
|
static void
|
2010-09-24 10:54:39 -07:00
|
|
|
MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
|
2010-04-08 05:54:17 -07:00
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
rt->gcNumber++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark phase.
|
|
|
|
*/
|
2010-07-26 11:44:04 -07:00
|
|
|
GCMarker gcmarker(cx);
|
|
|
|
JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker));
|
|
|
|
JS_ASSERT(gcmarker.getMarkColor() == BLACK);
|
|
|
|
rt->gcMarkingTracer = &gcmarker;
|
2010-09-24 10:54:39 -07:00
|
|
|
gcmarker.stackLimit = cx->stackLimit;
|
2010-09-07 14:08:20 -07:00
|
|
|
|
2010-08-05 05:16:56 -07:00
|
|
|
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
2010-09-24 10:54:39 -07:00
|
|
|
r.front()->clearMarkBitmap();
|
2010-09-07 14:08:20 -07:00
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
MarkRuntime(&gcmarker);
|
2010-04-23 15:15:42 -07:00
|
|
|
js_MarkScriptFilenames(rt);
|
2010-04-08 05:54:17 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark children of things that caused too deep recursion during the above
|
|
|
|
* tracing.
|
|
|
|
*/
|
2010-07-26 11:44:04 -07:00
|
|
|
gcmarker.markDelayedChildren();
|
2010-04-08 05:54:17 -07:00
|
|
|
|
2011-03-07 16:56:17 -08:00
|
|
|
/*
|
|
|
|
* Mark weak roots.
|
|
|
|
*/
|
|
|
|
while (true) {
|
|
|
|
if (!js_TraceWatchPoints(&gcmarker))
|
|
|
|
break;
|
|
|
|
gcmarker.markDelayedChildren();
|
|
|
|
}
|
|
|
|
|
2010-04-08 05:54:17 -07:00
|
|
|
rt->gcMarkingTracer = NULL;
|
|
|
|
|
2010-07-15 17:58:36 -07:00
|
|
|
if (rt->gcCallback)
|
|
|
|
(void) rt->gcCallback(cx, JSGC_MARK_END);
|
|
|
|
|
2010-04-08 05:54:17 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-09-07 14:08:20 -07:00
|
|
|
/*
|
|
|
|
* cx->gcBackgroundFree is set if we need several mark-and-sweep loops to
|
|
|
|
* finish the GC.
|
|
|
|
*/
|
|
|
|
if(!cx->gcBackgroundFree) {
|
|
|
|
/* Wait until the sweeping from the previois GC finishes. */
|
|
|
|
rt->gcHelperThread.waitBackgroundSweepEnd(rt);
|
|
|
|
cx->gcBackgroundFree = &rt->gcHelperThread;
|
|
|
|
}
|
2010-04-08 05:54:17 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sweep phase.
|
|
|
|
*
|
|
|
|
* Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
|
|
|
|
* so that any attempt to allocate a GC-thing from a finalizer will fail,
|
|
|
|
* rather than nest badly and leave the unmarked newborn to be swept.
|
|
|
|
*
|
2011-03-14 13:55:55 -07:00
|
|
|
* We first sweep atom state so we can use IsAboutToBeFinalized on
|
2010-07-26 11:44:04 -07:00
|
|
|
* JSString held in a hashtable to check if the hashtable entry can be
|
|
|
|
* freed. Note that even after the entry is freed, JSObject finalizers can
|
|
|
|
* continue to access the corresponding JSString* assuming that they are
|
|
|
|
* unique. This works since the atomization API must not be called during
|
|
|
|
* the GC.
|
2010-04-08 05:54:17 -07:00
|
|
|
*/
|
2010-04-22 05:35:21 -07:00
|
|
|
TIMESTAMP(startSweep);
|
2010-04-08 05:54:17 -07:00
|
|
|
js_SweepAtomState(cx);
|
|
|
|
|
|
|
|
/* Finalize watch points associated with unreachable objects. */
|
|
|
|
js_SweepWatchPoints(cx);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* Save the pre-sweep count of scope-mapped properties. */
|
2010-08-29 11:57:08 -07:00
|
|
|
rt->liveObjectPropsPreSweep = rt->liveObjectProps;
|
2010-04-08 05:54:17 -07:00
|
|
|
#endif
|
|
|
|
|
2011-01-08 20:06:29 -08:00
|
|
|
SweepCrossCompartmentWrappers(cx);
|
|
|
|
|
2010-04-08 05:54:17 -07:00
|
|
|
/*
|
2010-04-22 23:58:44 -07:00
|
|
|
* We finalize iterators before other objects so the iterator can use the
|
|
|
|
* object which properties it enumerates over to finalize the enumeration
|
2010-07-26 11:44:04 -07:00
|
|
|
* state. We finalize objects before other GC things to ensure that
|
|
|
|
* object's finalizer can access them even if they will be freed.
|
2010-04-08 05:54:17 -07:00
|
|
|
*/
|
2011-01-18 15:20:39 -08:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
|
|
|
|
(*c)->finalizeObjectArenaLists(cx);
|
2011-01-07 23:44:57 -08:00
|
|
|
|
2010-04-22 05:35:21 -07:00
|
|
|
TIMESTAMP(sweepObjectEnd);
|
2010-04-08 05:54:17 -07:00
|
|
|
|
2011-01-18 15:20:39 -08:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
|
|
|
|
(*c)->finalizeStringArenaLists(cx);
|
2010-09-07 14:08:20 -07:00
|
|
|
|
2010-04-22 05:35:21 -07:00
|
|
|
TIMESTAMP(sweepStringEnd);
|
2010-04-08 05:54:17 -07:00
|
|
|
|
|
|
|
/*
|
2010-08-29 11:57:08 -07:00
|
|
|
* Sweep the runtime's property trees after finalizing objects, in case any
|
2010-04-08 05:54:17 -07:00
|
|
|
* had watchpoints referencing tree nodes.
|
2011-02-04 10:59:07 -08:00
|
|
|
*
|
|
|
|
* Do this before sweeping compartments, so that we sweep all shapes in
|
|
|
|
* unreachable compartments.
|
2010-04-08 05:54:17 -07:00
|
|
|
*/
|
2011-02-04 10:59:07 -08:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
|
|
|
(*c)->propertyTree.sweepShapes(cx);
|
|
|
|
|
|
|
|
PropertyTree::dumpShapes(cx);
|
2011-02-23 19:58:19 -08:00
|
|
|
TIMESTAMP(sweepShapeEnd);
|
2011-02-04 10:59:07 -08:00
|
|
|
|
|
|
|
SweepCompartments(cx, gckind);
|
2010-04-08 05:54:17 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Sweep script filenames after sweeping functions in the generic loop
|
|
|
|
* above. In this way when a scripted function's finalizer destroys the
|
|
|
|
* script and calls rt->destroyScriptHook, the hook can still access the
|
|
|
|
* script's filename. See bug 323267.
|
|
|
|
*/
|
|
|
|
js_SweepScriptFilenames(rt);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy arenas after we finished the sweeping so finalizers can safely
|
2011-03-14 13:55:55 -07:00
|
|
|
* use IsAboutToBeFinalized().
|
2010-04-08 05:54:17 -07:00
|
|
|
*/
|
2010-09-24 10:54:39 -07:00
|
|
|
ExpireGCChunks(rt);
|
2010-04-22 05:35:21 -07:00
|
|
|
TIMESTAMP(sweepDestroyEnd);
|
2010-04-08 05:54:17 -07:00
|
|
|
|
|
|
|
if (rt->gcCallback)
|
|
|
|
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
|
|
|
|
#ifdef DEBUG_srcnotesize
|
|
|
|
{ extern void DumpSrcNoteSizeHist();
|
|
|
|
DumpSrcNoteSizeHist();
|
|
|
|
printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef JS_SCOPE_DEPTH_METER
|
2010-08-24 18:57:14 -07:00
|
|
|
DumpScopeDepthMeter(rt);
|
|
|
|
#endif
|
2010-04-08 05:54:17 -07:00
|
|
|
|
|
|
|
#ifdef JS_DUMP_LOOP_STATS
|
2010-08-24 18:57:14 -07:00
|
|
|
DumpLoopStats(rt);
|
|
|
|
#endif
|
2010-04-08 05:54:17 -07:00
|
|
|
}
|
|
|
|
|
2010-04-22 05:31:00 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-05-27 07:57:55 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the GC is running and we're called on another thread, wait for this GC
|
|
|
|
* activation to finish. We can safely wait here without fear of deadlock (in
|
|
|
|
* the case where we are called within a request on another thread's context)
|
|
|
|
* because the GC doesn't set rt->gcRunning until after it has waited for all
|
|
|
|
* active requests to end.
|
|
|
|
*
|
|
|
|
* We call here js_CurrentThreadId() after checking for rt->gcState to avoid
|
|
|
|
* an expensive call when the GC is not running.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
js_WaitForGC(JSRuntime *rt)
|
|
|
|
{
|
|
|
|
if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
|
|
|
|
do {
|
|
|
|
JS_AWAIT_GC_DONE(rt);
|
|
|
|
} while (rt->gcRunning);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-22 05:31:00 -07:00
|
|
|
/*
|
2010-05-14 06:55:17 -07:00
|
|
|
* GC is running on another thread. Temporarily suspend all requests running
|
|
|
|
* on the current thread and wait until the GC is done.
|
2010-04-22 05:31:00 -07:00
|
|
|
*/
|
|
|
|
static void
|
2010-05-22 12:49:58 -07:00
|
|
|
LetOtherGCFinish(JSContext *cx)
|
2010-04-22 05:31:00 -07:00
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
JS_ASSERT(rt->gcThread);
|
2010-05-14 06:55:17 -07:00
|
|
|
JS_ASSERT(cx->thread != rt->gcThread);
|
2010-04-22 05:31:00 -07:00
|
|
|
|
2010-10-22 10:48:06 -07:00
|
|
|
size_t requestDebit = cx->thread->data.requestDepth ? 1 : 0;
|
2010-04-22 05:31:00 -07:00
|
|
|
JS_ASSERT(requestDebit <= rt->requestCount);
|
|
|
|
#ifdef JS_TRACER
|
|
|
|
JS_ASSERT_IF(requestDebit == 0, !JS_ON_TRACE(cx));
|
|
|
|
#endif
|
|
|
|
if (requestDebit != 0) {
|
|
|
|
#ifdef JS_TRACER
|
|
|
|
if (JS_ON_TRACE(cx)) {
|
|
|
|
/*
|
|
|
|
* Leave trace before we decrease rt->requestCount and notify the
|
|
|
|
* GC. Otherwise the GC may start immediately after we unlock while
|
|
|
|
* this thread is still on trace.
|
|
|
|
*/
|
|
|
|
AutoUnlockGC unlock(rt);
|
|
|
|
LeaveTrace(cx);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
rt->requestCount -= requestDebit;
|
|
|
|
if (rt->requestCount == 0)
|
|
|
|
JS_NOTIFY_REQUEST_DONE(rt);
|
2010-05-22 12:49:58 -07:00
|
|
|
}
|
2010-04-22 05:31:00 -07:00
|
|
|
|
2010-05-22 12:49:58 -07:00
|
|
|
/*
|
|
|
|
* Check that we did not release the GC lock above and let the GC to
|
|
|
|
* finish before we wait.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(rt->gcThread);
|
2010-04-22 05:31:00 -07:00
|
|
|
|
2010-05-22 12:49:58 -07:00
|
|
|
/*
|
|
|
|
* Wait for GC to finish on the other thread, even if requestDebit is 0
|
|
|
|
* and even if GC has not started yet because the gcThread is waiting in
|
2010-08-30 11:46:18 -07:00
|
|
|
* AutoGCSession. This ensures that js_GC never returns without a full GC
|
2010-05-22 12:49:58 -07:00
|
|
|
* cycle happening.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
JS_AWAIT_GC_DONE(rt);
|
|
|
|
} while (rt->gcThread);
|
|
|
|
|
|
|
|
rt->requestCount += requestDebit;
|
2010-04-22 05:31:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
class AutoGCSession {
|
|
|
|
public:
|
|
|
|
explicit AutoGCSession(JSContext *cx);
|
|
|
|
~AutoGCSession();
|
|
|
|
|
|
|
|
private:
|
|
|
|
JSContext *context;
|
|
|
|
|
|
|
|
/* Disable copy constructor or assignments */
|
|
|
|
AutoGCSession(const AutoGCSession&);
|
|
|
|
void operator=(const AutoGCSession&);
|
|
|
|
};
|
|
|
|
|
2010-04-22 05:31:00 -07:00
|
|
|
/*
|
2010-08-30 11:46:18 -07:00
|
|
|
* Start a new GC session. Together with LetOtherGCFinish this function
|
|
|
|
* contains the rendezvous algorithm by which we stop the world for GC.
|
2010-04-22 05:31:00 -07:00
|
|
|
*
|
2010-05-14 06:55:17 -07:00
|
|
|
* This thread becomes the GC thread. Wait for all other threads to quiesce.
|
2010-08-30 11:46:18 -07:00
|
|
|
* Then set rt->gcRunning and return.
|
2010-04-22 05:31:00 -07:00
|
|
|
*/
|
2010-08-30 11:46:18 -07:00
|
|
|
AutoGCSession::AutoGCSession(JSContext *cx)
|
|
|
|
: context(cx)
|
2010-04-22 05:31:00 -07:00
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
2010-08-30 11:46:18 -07:00
|
|
|
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
if (rt->gcThread && rt->gcThread != cx->thread)
|
|
|
|
LetOtherGCFinish(cx);
|
|
|
|
#endif
|
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
JS_ASSERT(!rt->gcRunning);
|
2010-04-22 05:31:00 -07:00
|
|
|
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/* No other thread is in GC, so indicate that we're now in GC. */
|
2010-05-14 06:55:17 -07:00
|
|
|
JS_ASSERT(!rt->gcThread);
|
2010-04-22 05:31:00 -07:00
|
|
|
rt->gcThread = cx->thread;
|
|
|
|
|
|
|
|
/*
|
2010-07-22 13:59:59 -07:00
|
|
|
* Notify operation callbacks on other threads, which will give them a
|
|
|
|
* chance to yield their requests. Threads without requests perform their
|
|
|
|
* callback at some later point, which then will be unnecessary, but
|
|
|
|
* harmless.
|
2010-04-22 05:31:00 -07:00
|
|
|
*/
|
2010-07-22 13:59:59 -07:00
|
|
|
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
|
|
|
|
JSThread *thread = r.front().value;
|
|
|
|
if (thread != cx->thread)
|
2010-10-22 10:48:06 -07:00
|
|
|
thread->data.triggerOperationCallback(rt);
|
2010-07-22 13:59:59 -07:00
|
|
|
}
|
2010-04-22 05:31:00 -07:00
|
|
|
|
|
|
|
/*
|
2010-06-26 13:31:54 -07:00
|
|
|
* Discount the request on the current thread from contributing to
|
2010-05-14 06:55:17 -07:00
|
|
|
* rt->requestCount before we wait for all other requests to finish.
|
2010-04-22 05:31:00 -07:00
|
|
|
* JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on
|
|
|
|
* rt->requestCount transitions to 0.
|
|
|
|
*/
|
2010-10-22 10:48:06 -07:00
|
|
|
size_t requestDebit = cx->thread->data.requestDepth ? 1 : 0;
|
2010-04-22 05:31:00 -07:00
|
|
|
JS_ASSERT(requestDebit <= rt->requestCount);
|
|
|
|
if (requestDebit != rt->requestCount) {
|
|
|
|
rt->requestCount -= requestDebit;
|
|
|
|
|
|
|
|
do {
|
|
|
|
JS_AWAIT_REQUEST_DONE(rt);
|
|
|
|
} while (rt->requestCount > 0);
|
|
|
|
rt->requestCount += requestDebit;
|
|
|
|
}
|
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
#endif /* JS_THREADSAFE */
|
2010-04-22 05:31:00 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set rt->gcRunning here within the GC lock, and after waiting for any
|
2010-05-14 06:55:17 -07:00
|
|
|
* active requests to end. This way js_WaitForGC called outside a request
|
|
|
|
* would not block on the GC that is waiting for other requests to finish
|
|
|
|
* with rt->gcThread set while JS_BeginRequest would do such wait.
|
2010-04-22 05:31:00 -07:00
|
|
|
*/
|
2010-05-14 06:55:17 -07:00
|
|
|
rt->gcRunning = true;
|
2010-04-22 05:31:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* End the current GC session and allow other threads to proceed. */
|
2010-08-30 11:46:18 -07:00
|
|
|
AutoGCSession::~AutoGCSession()
|
2010-04-22 05:31:00 -07:00
|
|
|
{
|
2010-08-30 11:46:18 -07:00
|
|
|
JSRuntime *rt = context->runtime;
|
2010-05-14 06:55:17 -07:00
|
|
|
rt->gcRunning = false;
|
2010-04-22 05:31:00 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2010-08-30 11:46:18 -07:00
|
|
|
JS_ASSERT(rt->gcThread == context->thread);
|
2010-04-22 05:31:00 -07:00
|
|
|
rt->gcThread = NULL;
|
|
|
|
JS_NOTIFY_GC_DONE(rt);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-04-08 05:54:18 -07:00
|
|
|
/*
|
2010-05-14 06:55:17 -07:00
|
|
|
* GC, repeatedly if necessary, until we think we have not created any new
|
|
|
|
* garbage and no other threads are demanding more GC.
|
2010-04-08 05:54:18 -07:00
|
|
|
*/
|
2010-05-14 06:55:17 -07:00
|
|
|
static void
|
2011-01-07 23:44:57 -08:00
|
|
|
GCUntilDone(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_PARAM)
|
2010-04-08 05:54:18 -07:00
|
|
|
{
|
2010-05-14 06:55:17 -07:00
|
|
|
if (JS_ON_TRACE(cx))
|
|
|
|
return;
|
2010-04-08 05:54:18 -07:00
|
|
|
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
/* Recursive GC or a call from another thread restarts the GC cycle. */
|
2010-08-30 11:46:18 -07:00
|
|
|
if (rt->gcMarkAndSweep) {
|
2010-05-14 06:55:17 -07:00
|
|
|
rt->gcPoke = true;
|
2010-08-30 11:46:18 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_ASSERT(rt->gcThread);
|
|
|
|
if (rt->gcThread != cx->thread) {
|
|
|
|
/* We do not return until another GC finishes. */
|
|
|
|
LetOtherGCFinish(cx);
|
2010-08-29 12:41:24 -07:00
|
|
|
}
|
2010-08-30 11:46:18 -07:00
|
|
|
#endif
|
|
|
|
return;
|
2010-08-29 12:41:24 -07:00
|
|
|
}
|
2010-09-07 14:08:20 -07:00
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
AutoGCSession gcsession(cx);
|
2010-04-22 05:32:13 -07:00
|
|
|
|
2011-02-04 10:59:07 -08:00
|
|
|
/*
|
|
|
|
* We should not be depending on cx->compartment in the GC, so set it to
|
|
|
|
* NULL to look for violations.
|
|
|
|
*/
|
2011-03-14 10:37:05 -07:00
|
|
|
SwitchToCompartment sc(cx, (JSCompartment *)NULL);
|
2011-02-22 12:45:18 -08:00
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
JS_ASSERT(!rt->gcCurrentCompartment);
|
|
|
|
rt->gcCurrentCompartment = comp;
|
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
METER(rt->gcStats.poke++);
|
2010-04-22 05:32:13 -07:00
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
bool firstRun = true;
|
2010-08-30 11:46:18 -07:00
|
|
|
rt->gcMarkAndSweep = true;
|
2010-09-07 14:08:20 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_ASSERT(!cx->gcBackgroundFree);
|
|
|
|
#endif
|
2010-05-14 06:55:17 -07:00
|
|
|
do {
|
|
|
|
rt->gcPoke = false;
|
|
|
|
|
|
|
|
AutoUnlockGC unlock(rt);
|
|
|
|
if (firstRun) {
|
|
|
|
PreGCCleanup(cx, gckind);
|
2010-09-23 16:21:37 -07:00
|
|
|
TIMESTAMP(startMark);
|
2010-05-14 06:55:17 -07:00
|
|
|
firstRun = false;
|
2010-04-22 05:32:13 -07:00
|
|
|
}
|
2011-01-07 23:44:57 -08:00
|
|
|
|
|
|
|
if (comp)
|
|
|
|
MarkAndSweepCompartment(cx, comp, gckind GCTIMER_ARG);
|
|
|
|
else
|
|
|
|
MarkAndSweep(cx, gckind GCTIMER_ARG);
|
2010-05-14 06:55:17 -07:00
|
|
|
|
|
|
|
// GC again if:
|
|
|
|
// - another thread, not in a request, called js_GC
|
|
|
|
// - js_GC was called recursively
|
|
|
|
// - a finalizer called js_RemoveRoot or js_UnlockGCThingRT.
|
|
|
|
} while (rt->gcPoke);
|
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
|
|
|
|
cx->gcBackgroundFree = NULL;
|
|
|
|
rt->gcHelperThread.startBackgroundSweep(rt);
|
|
|
|
#endif
|
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
rt->gcMarkAndSweep = false;
|
2010-05-14 06:55:17 -07:00
|
|
|
rt->gcRegenShapes = false;
|
|
|
|
rt->setGCLastBytes(rt->gcBytes);
|
2011-01-07 23:44:57 -08:00
|
|
|
rt->gcCurrentCompartment = NULL;
|
|
|
|
|
2011-02-04 10:59:07 -08:00
|
|
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
2011-01-07 23:44:57 -08:00
|
|
|
(*c)->setGCLastBytes((*c)->gcBytes);
|
2010-04-22 05:32:13 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
2011-01-07 23:44:57 -08:00
|
|
|
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-04-22 05:31:05 -07:00
|
|
|
JSRuntime *rt = cx->runtime;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't collect garbage if the runtime isn't up, and cx is not the last
|
|
|
|
* context in the runtime. The last context must force a GC, and nothing
|
|
|
|
* should suppress that final collection or there may be shutdown leaks,
|
|
|
|
* or runtime bloat until the next context is created.
|
|
|
|
*/
|
|
|
|
if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT)
|
|
|
|
return;
|
2010-04-22 05:35:21 -07:00
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
RecordNativeStackTopForGC(cx);
|
|
|
|
|
2010-10-05 10:09:50 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
int stackDummy;
|
2010-10-19 14:25:26 -07:00
|
|
|
# if JS_STACK_GROWTH_DIRECTION > 0
|
2010-10-19 16:12:08 -07:00
|
|
|
/* cx->stackLimit is set to jsuword(-1) by default. */
|
|
|
|
JS_ASSERT_IF(cx->stackLimit != jsuword(-1),
|
2010-12-30 13:30:12 -08:00
|
|
|
JS_CHECK_STACK_SIZE(cx->stackLimit + (1 << 14), &stackDummy));
|
2010-10-19 14:25:26 -07:00
|
|
|
# else
|
2010-12-30 13:30:12 -08:00
|
|
|
/* -16k because it is possible to perform a GC during an overrecursion report. */
|
|
|
|
JS_ASSERT_IF(cx->stackLimit, JS_CHECK_STACK_SIZE(cx->stackLimit - (1 << 14), &stackDummy));
|
2010-10-19 14:25:26 -07:00
|
|
|
# endif
|
2010-10-05 10:09:50 -07:00
|
|
|
#endif
|
|
|
|
|
2010-04-22 05:35:21 -07:00
|
|
|
GCTIMER_BEGIN();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* Let the API user decide to defer a GC if it wants to (unless this
|
|
|
|
* is the last context). Invoke the callback regardless. Sample the
|
|
|
|
* callback in case we are freely racing with a JS_SetGCCallback{,RT}
|
|
|
|
* on another thread.
|
|
|
|
*/
|
|
|
|
if (JSGCCallback callback = rt->gcCallback) {
|
|
|
|
if (!callback(cx, JSGC_BEGIN) && gckind != GC_LAST_CONTEXT)
|
|
|
|
return;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-04-22 05:34:28 -07:00
|
|
|
{
|
|
|
|
/* Lock out other GC allocator and collector invocations. */
|
2010-10-07 13:43:52 -07:00
|
|
|
AutoLockGC lock(rt);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
GCUntilDone(cx, comp, gckind GCTIMER_ARG);
|
2010-05-14 06:55:17 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
/* We re-sample the callback again as the finalizers can change it. */
|
2010-10-07 13:43:52 -07:00
|
|
|
if (JSGCCallback callback = rt->gcCallback)
|
2010-05-14 06:55:17 -07:00
|
|
|
(void) callback(cx, JSGC_END);
|
2010-04-22 05:34:28 -07:00
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
/*
|
|
|
|
* On shutdown, iterate until the JSGC_END callback stops creating
|
|
|
|
* garbage.
|
|
|
|
*/
|
|
|
|
} while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
|
2010-09-24 10:54:39 -07:00
|
|
|
#ifdef JS_GCMETER
|
|
|
|
js_DumpGCStats(cx->runtime, stderr);
|
|
|
|
#endif
|
2010-04-22 05:35:21 -07:00
|
|
|
GCTIMER_END(gckind == GC_LAST_CONTEXT);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2010-05-14 06:55:17 -07:00
|
|
|
|
2010-06-17 15:23:17 -07:00
|
|
|
namespace js {
|
2010-09-24 10:54:39 -07:00
|
|
|
namespace gc {
|
2010-06-17 15:23:17 -07:00
|
|
|
|
2011-03-13 18:13:52 -07:00
|
|
|
void
|
|
|
|
MarkObjectSlots(JSTracer *trc, JSObject *obj)
|
|
|
|
{
|
|
|
|
JS_ASSERT(obj->slotSpan() <= obj->numSlots());
|
|
|
|
uint32 nslots = obj->slotSpan();
|
|
|
|
for (uint32 i = 0; i != nslots; ++i) {
|
|
|
|
const Value &v = obj->getSlot(i);
|
|
|
|
JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
|
|
|
|
MarkValueRaw(trc, v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-14 06:55:17 -07:00
|
|
|
bool
|
2010-06-17 15:23:17 -07:00
|
|
|
SetProtoCheckingForCycles(JSContext *cx, JSObject *obj, JSObject *proto)
|
2010-05-14 06:55:17 -07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This function cannot be called during the GC and always requires a
|
|
|
|
* request.
|
|
|
|
*/
|
|
|
|
#ifdef JS_THREADSAFE
|
2010-10-22 10:48:06 -07:00
|
|
|
JS_ASSERT(cx->thread->data.requestDepth);
|
2010-05-14 06:55:17 -07:00
|
|
|
|
2010-08-29 12:41:24 -07:00
|
|
|
/*
|
2010-08-30 11:46:18 -07:00
|
|
|
* This is only necessary if AutoGCSession below would wait for GC to
|
|
|
|
* finish on another thread, but to capture the minimal stack space and
|
|
|
|
* for code simplicity we do it here unconditionally.
|
2010-08-29 12:41:24 -07:00
|
|
|
*/
|
2010-08-30 11:46:18 -07:00
|
|
|
RecordNativeStackTopForGC(cx);
|
2010-08-29 12:41:24 -07:00
|
|
|
#endif
|
2010-05-14 06:55:17 -07:00
|
|
|
|
2010-08-30 11:46:18 -07:00
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
AutoGCSession gcsession(cx);
|
|
|
|
AutoUnlockGC unlock(rt);
|
|
|
|
|
|
|
|
bool cycle = false;
|
|
|
|
for (JSObject *obj2 = proto; obj2;) {
|
|
|
|
if (obj2 == obj) {
|
|
|
|
cycle = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
obj2 = obj2->getProto();
|
|
|
|
}
|
|
|
|
if (!cycle)
|
|
|
|
obj->setProto(proto);
|
|
|
|
|
|
|
|
return !cycle;
|
|
|
|
}
|
2010-05-14 06:55:17 -07:00
|
|
|
|
2010-06-04 16:32:10 -07:00
|
|
|
JSCompartment *
|
2010-06-24 14:45:32 -07:00
|
|
|
NewCompartment(JSContext *cx, JSPrincipals *principals)
|
2010-06-04 16:32:10 -07:00
|
|
|
{
|
|
|
|
JSRuntime *rt = cx->runtime;
|
2011-01-17 19:44:10 -08:00
|
|
|
JSCompartment *compartment = js_new<JSCompartment>(rt);
|
2010-06-23 14:35:10 -07:00
|
|
|
if (!compartment || !compartment->init()) {
|
2011-01-17 19:44:10 -08:00
|
|
|
js_delete(compartment);
|
2010-06-04 16:32:10 -07:00
|
|
|
JS_ReportOutOfMemory(cx);
|
2010-07-19 13:36:49 -07:00
|
|
|
return NULL;
|
2010-06-04 16:32:10 -07:00
|
|
|
}
|
|
|
|
|
2010-06-24 14:45:32 -07:00
|
|
|
if (principals) {
|
|
|
|
compartment->principals = principals;
|
|
|
|
JSPRINCIPALS_HOLD(cx, principals);
|
|
|
|
}
|
|
|
|
|
2011-01-07 23:44:57 -08:00
|
|
|
compartment->setGCLastBytes(8192);
|
|
|
|
|
2010-07-19 13:36:49 -07:00
|
|
|
{
|
|
|
|
AutoLockGC lock(rt);
|
2010-06-04 16:32:10 -07:00
|
|
|
|
2010-07-19 13:36:49 -07:00
|
|
|
if (!rt->compartments.append(compartment)) {
|
|
|
|
AutoUnlockGC unlock(rt);
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
JSCompartmentCallback callback = rt->compartmentCallback;
|
|
|
|
if (callback && !callback(cx, compartment, JSCOMPARTMENT_NEW)) {
|
|
|
|
AutoLockGC lock(rt);
|
|
|
|
rt->compartments.popBack();
|
|
|
|
return NULL;
|
2010-06-04 16:32:10 -07:00
|
|
|
}
|
|
|
|
return compartment;
|
|
|
|
}
|
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
} /* namespace gc */
|
|
|
|
|
|
|
|
void
|
|
|
|
TraceRuntime(JSTracer *trc)
|
|
|
|
{
|
|
|
|
LeaveTrace(trc->context);
|
|
|
|
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
{
|
|
|
|
JSContext *cx = trc->context;
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
AutoLockGC lock(rt);
|
2010-10-07 13:43:52 -07:00
|
|
|
|
2010-09-24 10:54:39 -07:00
|
|
|
if (rt->gcThread != cx->thread) {
|
|
|
|
AutoGCSession gcsession(cx);
|
|
|
|
AutoUnlockGC unlock(rt);
|
|
|
|
RecordNativeStackTopForGC(trc->context);
|
|
|
|
MarkRuntime(trc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
RecordNativeStackTopForGC(trc->context);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calls from inside a normal GC or a recursive calls are OK and do not
|
|
|
|
* require session setup.
|
|
|
|
*/
|
|
|
|
MarkRuntime(trc);
|
2010-06-04 16:32:10 -07:00
|
|
|
}
|
2010-09-24 10:54:39 -07:00
|
|
|
|
|
|
|
} /* namespace js */
|