Bug 668855, part 1: add JS weak map tracing interface. r=billm

This commit is contained in:
Andrew McCreight 2011-11-24 07:35:56 -05:00
parent dcf73a7c49
commit 8a8de0249a
5 changed files with 107 additions and 11 deletions

View File

@ -41,6 +41,7 @@
#include "jscompartment.h"
#include "jsfriendapi.h"
#include "jswrapper.h"
#include "jsweakmap.h"
#include "jsobjinlines.h"
@ -223,6 +224,12 @@ JS_GetCustomIteratorCount(JSContext *cx)
return sCustomIteratorCount;
}
void
js::TraceWeakMaps(WeakMapTracer *trc)
{
WeakMapBase::traceAllMappings(trc);
}
JS_FRIEND_API(void)
JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback)
{

View File

@ -205,6 +205,30 @@ JS_FRIEND_API(JSBool) obj_defineSetter(JSContext *cx, uintN argc, js::Value *vp)
extern JS_FRIEND_API(bool)
CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname);
struct WeakMapTracer;
/*
* Weak map tracer callback, called once for every binding of every
* weak map that was live at the time of the last garbage collection.
*
* m will be NULL if the weak map is not contained in a JS Object.
*/
typedef void
(* WeakMapTraceCallback)(WeakMapTracer *trc, JSObject *m,
void *k, JSGCTraceKind kkind,
void *v, JSGCTraceKind vkind);
struct WeakMapTracer {
JSContext *context;
WeakMapTraceCallback callback;
WeakMapTracer(JSContext *cx, WeakMapTraceCallback cb)
: context(cx), callback(cb) {}
};
extern JS_FRIEND_API(void)
TraceWeakMaps(WeakMapTracer *trc);
/*
* Shadow declarations of JS internal structures, for access by inline access
* functions below. Do not use these structures in any other way. When adding

View File

@ -2673,6 +2673,9 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
rt->gcIsNeeded = false;
rt->gcTriggerCompartment = NULL;
/* Reset weak map list. */
rt->gcWeakMapList = NULL;
/* Reset malloc counter. */
rt->resetGCMallocBytes();
@ -2935,7 +2938,6 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
rt->gcRegenShapes = false;
rt->setGCLastBytes(rt->gcBytes, gckind);
rt->gcCurrentCompartment = NULL;
rt->gcWeakMapList = NULL;
for (CompartmentsIter c(rt); !c.done(); c.next())
c->setGCLastBytes(c->gcBytes, gckind);

View File

@ -78,6 +78,14 @@ WeakMapBase::sweepAll(JSTracer *tracer)
m->sweep(tracer);
}
void
WeakMapBase::traceAllMappings(WeakMapTracer *tracer)
{
JSRuntime *rt = tracer->context->runtime;
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
m->traceMappings(tracer);
}
} /* namespace js */
typedef WeakMap<HeapPtr<JSObject>, HeapValue> ObjectValueMap;
@ -215,7 +223,7 @@ WeakMap_set(JSContext *cx, uintN argc, Value *vp)
ObjectValueMap *map = GetObjectMap(obj);
if (!map) {
map = cx->new_<ObjectValueMap>(cx);
map = cx->new_<ObjectValueMap>(cx, obj);
if (!map->init()) {
cx->delete_(map);
goto out_of_memory;

View File

@ -43,6 +43,7 @@
#define jsweakmap_h___
#include "jsapi.h"
#include "jsfriendapi.h"
#include "jscntxt.h"
#include "jsobj.h"
#include "jsgcmark.h"
@ -101,11 +102,15 @@ namespace js {
// provides default types for WeakMap's MarkPolicy template parameter.
template <class Type> class DefaultMarkPolicy;
// A policy template holding default tracing algorithms for common type combinations. This
// provides default types for WeakMap's TracePolicy template parameter.
template <class Key, class Value> class DefaultTracePolicy;
// Common base class for all WeakMap specializations. The collector uses this to call
// their markIteratively and sweep methods.
class WeakMapBase {
public:
WeakMapBase() : next(NULL) { }
WeakMapBase(JSObject *memOf) : memberOf(memOf), next(NULL) { }
virtual ~WeakMapBase() { }
void trace(JSTracer *tracer) {
@ -121,8 +126,8 @@ class WeakMapBase {
} else {
// If we're not actually doing garbage collection, the keys won't be marked
// nicely as needed by the true ephemeral marking algorithm --- custom tracers
// must use their own means for cycle detection. So here we do a conservative
// approximation: pretend all keys are live.
// such as the cycle collector must use their own means for cycle detection.
// So here we do a conservative approximation: pretend all keys are live.
if (tracer->eagerlyTraceWeakMaps)
nonMarkingTrace(tracer);
}
@ -140,12 +145,19 @@ class WeakMapBase {
// garbage collection.
static void sweepAll(JSTracer *tracer);
// Trace all delayed weak map bindings. Used by the cycle collector.
static void traceAllMappings(WeakMapTracer *tracer);
protected:
// Instance member functions called by the above. Instantiations of WeakMap override
// these with definitions appropriate for their Key and Value types.
virtual void nonMarkingTrace(JSTracer *tracer) = 0;
virtual bool markIteratively(JSTracer *tracer) = 0;
virtual void sweep(JSTracer *tracer) = 0;
virtual void traceMappings(WeakMapTracer *tracer) = 0;
// Object that this weak map is part of, if any.
JSObject *memberOf;
private:
// Link in a list of WeakMaps to mark iteratively and sweep in this garbage
@ -156,7 +168,8 @@ class WeakMapBase {
template <class Key, class Value,
class HashPolicy = DefaultHasher<Key>,
class KeyMarkPolicy = DefaultMarkPolicy<Key>,
class ValueMarkPolicy = DefaultMarkPolicy<Value> >
class ValueMarkPolicy = DefaultMarkPolicy<Value>,
class TracePolicy = DefaultTracePolicy<Key, Value> >
class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase {
private:
typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
@ -165,8 +178,8 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
public:
typedef typename Base::Range Range;
explicit WeakMap(JSRuntime *rt) : Base(rt) { }
explicit WeakMap(JSContext *cx) : Base(cx) { }
explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { }
explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { }
// Use with caution, as result can be affected by garbage collection.
Range nondeterministicAll() {
@ -191,9 +204,8 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
if (kp.isMarked(k)) {
markedAny |= vp.mark(v);
} else if (kp.overrideKeyMarking(k)) {
// We always mark wrapped natives. This will cause leaks, but WeakMap+CC
// integration is currently busted anyways. When WeakMap+CC integration is
// fixed in Bug 668855, XPC wrapped natives should only be marked during
// We always mark wrapped natives. This will cause leaks. Bug 680937
// will fix this so XPC wrapped natives are only marked during
// non-BLACK marking (ie grey marking).
kp.mark(k);
vp.mark(v);
@ -225,6 +237,13 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
}
#endif
}
// mapObj can be NULL, which means that the map is not part of a JSObject.
void traceMappings(WeakMapTracer *tracer) {
TracePolicy t(tracer);
for (Range r = Base::all(); !r.empty(); r.popFront())
t.traceMapping(memberOf, r.front().key, r.front().value);
}
};
template <>
@ -289,6 +308,42 @@ class DefaultMarkPolicy<HeapPtrScript> {
bool overrideKeyMarking(const HeapPtrScript &k) { return false; }
};
// Default trace policies
template <>
class DefaultTracePolicy<HeapPtrObject, HeapValue> {
private:
WeakMapTracer *tracer;
public:
DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
void traceMapping(JSObject *m, const HeapPtr<JSObject> &k, HeapValue &v) {
if (v.isMarkable())
tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.toGCThing(), v.gcKind());
}
};
template <>
class DefaultTracePolicy<HeapPtrObject, HeapPtrObject> {
private:
WeakMapTracer *tracer;
public:
DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
void traceMapping(JSObject *m, const HeapPtrObject &k, const HeapPtrObject &v) {
tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.get(), JSTRACE_OBJECT);
}
};
template <>
class DefaultTracePolicy<HeapPtrScript, HeapPtrObject> {
private:
WeakMapTracer *tracer;
public:
DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
void traceMapping(JSObject *m, const HeapPtrScript &k, const HeapPtrObject &v) {
tracer->callback(tracer, m, k.get(), JSTRACE_SCRIPT, v.get(), JSTRACE_OBJECT);
}
};
}
extern JSObject *