xpcom/base/CycleCollectedJSRuntime.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 // We're dividing JS objects into 3 categories:
michael@0 8 //
michael@0 9 // 1. "real" roots, held by the JS engine itself or rooted through the root
michael@0 10 // and lock JS APIs. Roots from this category are considered black in the
michael@0 11 // cycle collector, any cycle they participate in is uncollectable.
michael@0 12 //
michael@0 13 // 2. certain roots held by C++ objects that are guaranteed to be alive.
michael@0 14 // Roots from this category are considered black in the cycle collector,
michael@0 15 // and any cycle they participate in is uncollectable. These roots are
michael@0 16 // traced from TraceNativeBlackRoots.
michael@0 17 //
michael@0 18 // 3. all other roots held by C++ objects that participate in cycle
michael@0 19 // collection, held by us (see TraceNativeGrayRoots). Roots from this
michael@0 20 // category are considered grey in the cycle collector; whether or not
michael@0 21 // they are collected depends on the objects that hold them.
michael@0 22 //
michael@0 23 // Note that if a root is in multiple categories the fact that it is in
michael@0 24 // category 1 or 2 that takes precedence, so it will be considered black.
michael@0 25 //
michael@0 26 // During garbage collection we switch to an additional mark color (gray)
michael@0 27 // when tracing inside TraceNativeGrayRoots. This allows us to walk those
michael@0 28 // roots later on and add all objects reachable only from them to the
michael@0 29 // cycle collector.
michael@0 30 //
michael@0 31 // Phases:
michael@0 32 //
michael@0 33 // 1. marking of the roots in category 1 by having the JS GC do its marking
michael@0 34 // 2. marking of the roots in category 2 by having the JS GC call us back
michael@0 35 // (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
michael@0 36 // 3. marking of the roots in category 3 by TraceNativeGrayRoots using an
michael@0 37 // additional color (gray).
michael@0 38 // 4. end of GC, GC can sweep its heap
michael@0 39 //
michael@0 40 // At some later point, when the cycle collector runs:
michael@0 41 //
michael@0 42 // 5. walk gray objects and add them to the cycle collector, cycle collect
michael@0 43 //
michael@0 44 // JS objects that are part of cycles the cycle collector breaks will be
michael@0 45 // collected by the next JS GC.
michael@0 46 //
michael@0 47 // If WantAllTraces() is false the cycle collector will not traverse roots
michael@0 48 // from category 1 or any JS objects held by them. Any JS objects they hold
michael@0 49 // will already be marked by the JS GC and will thus be colored black
michael@0 50 // themselves. Any C++ objects they hold will have a missing (untraversed)
michael@0 51 // edge from the JS object to the C++ object and so it will be marked black
michael@0 52 // too. This decreases the number of objects that the cycle collector has to
michael@0 53 // deal with.
michael@0 54 // To improve debugging, if WantAllTraces() is true all JS objects are
michael@0 55 // traversed.
michael@0 56
michael@0 57 #include "mozilla/CycleCollectedJSRuntime.h"
michael@0 58 #include <algorithm>
michael@0 59 #include "mozilla/ArrayUtils.h"
michael@0 60 #include "mozilla/MemoryReporting.h"
michael@0 61 #include "mozilla/dom/BindingUtils.h"
michael@0 62 #include "mozilla/dom/DOMJSClass.h"
michael@0 63 #include "mozilla/dom/ScriptSettings.h"
michael@0 64 #include "jsprf.h"
michael@0 65 #include "nsCycleCollectionNoteRootCallback.h"
michael@0 66 #include "nsCycleCollectionParticipant.h"
michael@0 67 #include "nsCycleCollector.h"
michael@0 68 #include "nsDOMJSUtils.h"
michael@0 69 #include "nsIException.h"
michael@0 70 #include "nsThreadUtils.h"
michael@0 71 #include "xpcpublic.h"
michael@0 72
michael@0 73 using namespace mozilla;
michael@0 74 using namespace mozilla::dom;
michael@0 75
michael@0 76 namespace mozilla {
michael@0 77
michael@0 78 struct DeferredFinalizeFunctionHolder
michael@0 79 {
michael@0 80 DeferredFinalizeFunction run;
michael@0 81 void *data;
michael@0 82 };
michael@0 83
michael@0 84 class IncrementalFinalizeRunnable : public nsRunnable
michael@0 85 {
michael@0 86 typedef nsAutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
michael@0 87 typedef CycleCollectedJSRuntime::DeferredFinalizerTable DeferredFinalizerTable;
michael@0 88
michael@0 89 CycleCollectedJSRuntime* mRuntime;
michael@0 90 nsTArray<nsISupports*> mSupports;
michael@0 91 DeferredFinalizeArray mDeferredFinalizeFunctions;
michael@0 92 uint32_t mFinalizeFunctionToRun;
michael@0 93
michael@0 94 static const PRTime SliceMillis = 10; /* ms */
michael@0 95
michael@0 96 static PLDHashOperator
michael@0 97 DeferredFinalizerEnumerator(DeferredFinalizeFunction& aFunction,
michael@0 98 void*& aData,
michael@0 99 void* aClosure);
michael@0 100
michael@0 101 public:
michael@0 102 IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
michael@0 103 nsTArray<nsISupports*>& mSupports,
michael@0 104 DeferredFinalizerTable& aFinalizerTable);
michael@0 105 virtual ~IncrementalFinalizeRunnable();
michael@0 106
michael@0 107 void ReleaseNow(bool aLimited);
michael@0 108
michael@0 109 NS_DECL_NSIRUNNABLE
michael@0 110 };
michael@0 111
michael@0 112 } // namespace mozilla
michael@0 113
michael@0 114 inline bool
michael@0 115 AddToCCKind(JSGCTraceKind kind)
michael@0 116 {
michael@0 117 return kind == JSTRACE_OBJECT || kind == JSTRACE_SCRIPT;
michael@0 118 }
michael@0 119
michael@0 120 static void
michael@0 121 TraceWeakMappingChild(JSTracer* trc, void** thingp, JSGCTraceKind kind);
michael@0 122
michael@0 123 struct NoteWeakMapChildrenTracer : public JSTracer
michael@0 124 {
michael@0 125 NoteWeakMapChildrenTracer(JSRuntime *rt, nsCycleCollectionNoteRootCallback& cb)
michael@0 126 : JSTracer(rt, TraceWeakMappingChild), mCb(cb)
michael@0 127 {
michael@0 128 }
michael@0 129 nsCycleCollectionNoteRootCallback& mCb;
michael@0 130 bool mTracedAny;
michael@0 131 JSObject* mMap;
michael@0 132 void* mKey;
michael@0 133 void* mKeyDelegate;
michael@0 134 };
michael@0 135
michael@0 136 static void
michael@0 137 TraceWeakMappingChild(JSTracer* trc, void** thingp, JSGCTraceKind kind)
michael@0 138 {
michael@0 139 MOZ_ASSERT(trc->callback == TraceWeakMappingChild);
michael@0 140 void* thing = *thingp;
michael@0 141 NoteWeakMapChildrenTracer* tracer =
michael@0 142 static_cast<NoteWeakMapChildrenTracer*>(trc);
michael@0 143
michael@0 144 if (kind == JSTRACE_STRING) {
michael@0 145 return;
michael@0 146 }
michael@0 147
michael@0 148 if (!xpc_IsGrayGCThing(thing) && !tracer->mCb.WantAllTraces()) {
michael@0 149 return;
michael@0 150 }
michael@0 151
michael@0 152 if (AddToCCKind(kind)) {
michael@0 153 tracer->mCb.NoteWeakMapping(tracer->mMap, tracer->mKey, tracer->mKeyDelegate, thing);
michael@0 154 tracer->mTracedAny = true;
michael@0 155 } else {
michael@0 156 JS_TraceChildren(trc, thing, kind);
michael@0 157 }
michael@0 158 }
michael@0 159
michael@0 160 struct NoteWeakMapsTracer : public js::WeakMapTracer
michael@0 161 {
michael@0 162 NoteWeakMapsTracer(JSRuntime* rt, js::WeakMapTraceCallback cb,
michael@0 163 nsCycleCollectionNoteRootCallback& cccb)
michael@0 164 : js::WeakMapTracer(rt, cb), mCb(cccb), mChildTracer(rt, cccb)
michael@0 165 {
michael@0 166 }
michael@0 167 nsCycleCollectionNoteRootCallback& mCb;
michael@0 168 NoteWeakMapChildrenTracer mChildTracer;
michael@0 169 };
michael@0 170
michael@0 171 static void
michael@0 172 TraceWeakMapping(js::WeakMapTracer* trc, JSObject* m,
michael@0 173 void* k, JSGCTraceKind kkind,
michael@0 174 void* v, JSGCTraceKind vkind)
michael@0 175 {
michael@0 176 MOZ_ASSERT(trc->callback == TraceWeakMapping);
michael@0 177 NoteWeakMapsTracer* tracer = static_cast<NoteWeakMapsTracer* >(trc);
michael@0 178
michael@0 179 // If nothing that could be held alive by this entry is marked gray, return.
michael@0 180 if ((!k || !xpc_IsGrayGCThing(k)) && MOZ_LIKELY(!tracer->mCb.WantAllTraces())) {
michael@0 181 if (!v || !xpc_IsGrayGCThing(v) || vkind == JSTRACE_STRING) {
michael@0 182 return;
michael@0 183 }
michael@0 184 }
michael@0 185
michael@0 186 // The cycle collector can only properly reason about weak maps if it can
michael@0 187 // reason about the liveness of their keys, which in turn requires that
michael@0 188 // the key can be represented in the cycle collector graph. All existing
michael@0 189 // uses of weak maps use either objects or scripts as keys, which are okay.
michael@0 190 MOZ_ASSERT(AddToCCKind(kkind));
michael@0 191
michael@0 192 // As an emergency fallback for non-debug builds, if the key is not
michael@0 193 // representable in the cycle collector graph, we treat it as marked. This
michael@0 194 // can cause leaks, but is preferable to ignoring the binding, which could
michael@0 195 // cause the cycle collector to free live objects.
michael@0 196 if (!AddToCCKind(kkind)) {
michael@0 197 k = nullptr;
michael@0 198 }
michael@0 199
michael@0 200 JSObject* kdelegate = nullptr;
michael@0 201 if (k && kkind == JSTRACE_OBJECT) {
michael@0 202 kdelegate = js::GetWeakmapKeyDelegate((JSObject*)k);
michael@0 203 }
michael@0 204
michael@0 205 if (AddToCCKind(vkind)) {
michael@0 206 tracer->mCb.NoteWeakMapping(m, k, kdelegate, v);
michael@0 207 } else {
michael@0 208 tracer->mChildTracer.mTracedAny = false;
michael@0 209 tracer->mChildTracer.mMap = m;
michael@0 210 tracer->mChildTracer.mKey = k;
michael@0 211 tracer->mChildTracer.mKeyDelegate = kdelegate;
michael@0 212
michael@0 213 if (v && vkind != JSTRACE_STRING) {
michael@0 214 JS_TraceChildren(&tracer->mChildTracer, v, vkind);
michael@0 215 }
michael@0 216
michael@0 217 // The delegate could hold alive the key, so report something to the CC
michael@0 218 // if we haven't already.
michael@0 219 if (!tracer->mChildTracer.mTracedAny && k && xpc_IsGrayGCThing(k) && kdelegate) {
michael@0 220 tracer->mCb.NoteWeakMapping(m, k, kdelegate, nullptr);
michael@0 221 }
michael@0 222 }
michael@0 223 }
michael@0 224
michael@0 225 // This is based on the logic in TraceWeakMapping.
michael@0 226 struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
michael@0 227 {
michael@0 228 FixWeakMappingGrayBitsTracer(JSRuntime* rt)
michael@0 229 : js::WeakMapTracer(rt, FixWeakMappingGrayBits)
michael@0 230 {}
michael@0 231
michael@0 232 void
michael@0 233 FixAll()
michael@0 234 {
michael@0 235 do {
michael@0 236 mAnyMarked = false;
michael@0 237 js::TraceWeakMaps(this);
michael@0 238 } while (mAnyMarked);
michael@0 239 }
michael@0 240
michael@0 241 private:
michael@0 242
michael@0 243 static void
michael@0 244 FixWeakMappingGrayBits(js::WeakMapTracer* trc, JSObject* m,
michael@0 245 void* k, JSGCTraceKind kkind,
michael@0 246 void* v, JSGCTraceKind vkind)
michael@0 247 {
michael@0 248 MOZ_ASSERT(!JS::IsIncrementalGCInProgress(trc->runtime),
michael@0 249 "Don't call FixWeakMappingGrayBits during a GC.");
michael@0 250
michael@0 251 FixWeakMappingGrayBitsTracer* tracer = static_cast<FixWeakMappingGrayBitsTracer*>(trc);
michael@0 252
michael@0 253 // If nothing that could be held alive by this entry is marked gray, return.
michael@0 254 bool delegateMightNeedMarking = k && xpc_IsGrayGCThing(k);
michael@0 255 bool valueMightNeedMarking = v && xpc_IsGrayGCThing(v) && vkind != JSTRACE_STRING;
michael@0 256 if (!delegateMightNeedMarking && !valueMightNeedMarking) {
michael@0 257 return;
michael@0 258 }
michael@0 259
michael@0 260 if (!AddToCCKind(kkind)) {
michael@0 261 k = nullptr;
michael@0 262 }
michael@0 263
michael@0 264 if (delegateMightNeedMarking && kkind == JSTRACE_OBJECT) {
michael@0 265 JSObject* kdelegate = js::GetWeakmapKeyDelegate((JSObject*)k);
michael@0 266 if (kdelegate && !xpc_IsGrayGCThing(kdelegate)) {
michael@0 267 if (JS::UnmarkGrayGCThingRecursively(k, JSTRACE_OBJECT)) {
michael@0 268 tracer->mAnyMarked = true;
michael@0 269 }
michael@0 270 }
michael@0 271 }
michael@0 272
michael@0 273 if (v && xpc_IsGrayGCThing(v) &&
michael@0 274 (!k || !xpc_IsGrayGCThing(k)) &&
michael@0 275 (!m || !xpc_IsGrayGCThing(m)) &&
michael@0 276 vkind != JSTRACE_SHAPE) {
michael@0 277 if (JS::UnmarkGrayGCThingRecursively(v, vkind)) {
michael@0 278 tracer->mAnyMarked = true;
michael@0 279 }
michael@0 280 }
michael@0 281 }
michael@0 282
michael@0 283 bool mAnyMarked;
michael@0 284 };
michael@0 285
michael@0 286 struct Closure
michael@0 287 {
michael@0 288 Closure(nsCycleCollectionNoteRootCallback* aCb)
michael@0 289 : mCycleCollectionEnabled(true), mCb(aCb)
michael@0 290 {
michael@0 291 }
michael@0 292
michael@0 293 bool mCycleCollectionEnabled;
michael@0 294 nsCycleCollectionNoteRootCallback* mCb;
michael@0 295 };
michael@0 296
michael@0 297 static void
michael@0 298 CheckParticipatesInCycleCollection(void* aThing, const char* aName, void* aClosure)
michael@0 299 {
michael@0 300 Closure* closure = static_cast<Closure*>(aClosure);
michael@0 301
michael@0 302 if (closure->mCycleCollectionEnabled) {
michael@0 303 return;
michael@0 304 }
michael@0 305
michael@0 306 if (AddToCCKind(js::GCThingTraceKind(aThing)) &&
michael@0 307 xpc_IsGrayGCThing(aThing))
michael@0 308 {
michael@0 309 closure->mCycleCollectionEnabled = true;
michael@0 310 }
michael@0 311 }
michael@0 312
michael@0 313 static PLDHashOperator
michael@0 314 NoteJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg)
michael@0 315 {
michael@0 316 Closure *closure = static_cast<Closure*>(arg);
michael@0 317
michael@0 318 bool noteRoot;
michael@0 319 if (MOZ_UNLIKELY(closure->mCb->WantAllTraces())) {
michael@0 320 noteRoot = true;
michael@0 321 } else {
michael@0 322 closure->mCycleCollectionEnabled = false;
michael@0 323 tracer->Trace(holder, TraceCallbackFunc(CheckParticipatesInCycleCollection), closure);
michael@0 324 noteRoot = closure->mCycleCollectionEnabled;
michael@0 325 }
michael@0 326
michael@0 327 if (noteRoot) {
michael@0 328 closure->mCb->NoteNativeRoot(holder, tracer);
michael@0 329 }
michael@0 330
michael@0 331 return PL_DHASH_NEXT;
michael@0 332 }
michael@0 333
michael@0 334 NS_IMETHODIMP
michael@0 335 JSGCThingParticipant::Traverse(void* p, nsCycleCollectionTraversalCallback& cb)
michael@0 336 {
michael@0 337 CycleCollectedJSRuntime* runtime = reinterpret_cast<CycleCollectedJSRuntime*>
michael@0 338 (reinterpret_cast<char*>(this) -
michael@0 339 offsetof(CycleCollectedJSRuntime, mGCThingCycleCollectorGlobal));
michael@0 340
michael@0 341 runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL,
michael@0 342 p, js::GCThingTraceKind(p), cb);
michael@0 343 return NS_OK;
michael@0 344 }
michael@0 345
michael@0 346 // NB: This is only used to initialize the participant in
michael@0 347 // CycleCollectedJSRuntime. It should never be used directly.
michael@0 348 static JSGCThingParticipant sGCThingCycleCollectorGlobal;
michael@0 349
michael@0 350 NS_IMETHODIMP
michael@0 351 JSZoneParticipant::Traverse(void* p, nsCycleCollectionTraversalCallback& cb)
michael@0 352 {
michael@0 353 CycleCollectedJSRuntime* runtime = reinterpret_cast<CycleCollectedJSRuntime*>
michael@0 354 (reinterpret_cast<char*>(this) -
michael@0 355 offsetof(CycleCollectedJSRuntime, mJSZoneCycleCollectorGlobal));
michael@0 356
michael@0 357 MOZ_ASSERT(!cb.WantAllTraces());
michael@0 358 JS::Zone* zone = static_cast<JS::Zone*>(p);
michael@0 359
michael@0 360 runtime->TraverseZone(zone, cb);
michael@0 361 return NS_OK;
michael@0 362 }
michael@0 363
michael@0 364 static void
michael@0 365 NoteJSChildTracerShim(JSTracer* aTrc, void** aThingp, JSGCTraceKind aTraceKind);
michael@0 366
michael@0 367 struct TraversalTracer : public JSTracer
michael@0 368 {
michael@0 369 TraversalTracer(JSRuntime *rt, nsCycleCollectionTraversalCallback& aCb)
michael@0 370 : JSTracer(rt, NoteJSChildTracerShim, DoNotTraceWeakMaps), mCb(aCb)
michael@0 371 {
michael@0 372 }
michael@0 373 nsCycleCollectionTraversalCallback& mCb;
michael@0 374 };
michael@0 375
michael@0 376 static void
michael@0 377 NoteJSChild(JSTracer* aTrc, void* aThing, JSGCTraceKind aTraceKind)
michael@0 378 {
michael@0 379 TraversalTracer* tracer = static_cast<TraversalTracer*>(aTrc);
michael@0 380
michael@0 381 // Don't traverse non-gray objects, unless we want all traces.
michael@0 382 if (!xpc_IsGrayGCThing(aThing) && !tracer->mCb.WantAllTraces()) {
michael@0 383 return;
michael@0 384 }
michael@0 385
michael@0 386 /*
michael@0 387 * This function needs to be careful to avoid stack overflow. Normally, when
michael@0 388 * AddToCCKind is true, the recursion terminates immediately as we just add
michael@0 389 * |thing| to the CC graph. So overflow is only possible when there are long
michael@0 390 * chains of non-AddToCCKind GC things. Currently, this only can happen via
michael@0 391 * shape parent pointers. The special JSTRACE_SHAPE case below handles
michael@0 392 * parent pointers iteratively, rather than recursively, to avoid overflow.
michael@0 393 */
michael@0 394 if (AddToCCKind(aTraceKind)) {
michael@0 395 if (MOZ_UNLIKELY(tracer->mCb.WantDebugInfo())) {
michael@0 396 // based on DumpNotify in jsapi.cpp
michael@0 397 if (tracer->debugPrinter()) {
michael@0 398 char buffer[200];
michael@0 399 tracer->debugPrinter()(aTrc, buffer, sizeof(buffer));
michael@0 400 tracer->mCb.NoteNextEdgeName(buffer);
michael@0 401 } else if (tracer->debugPrintIndex() != (size_t)-1) {
michael@0 402 char buffer[200];
michael@0 403 JS_snprintf(buffer, sizeof(buffer), "%s[%lu]",
michael@0 404 static_cast<const char *>(tracer->debugPrintArg()),
michael@0 405 tracer->debugPrintIndex());
michael@0 406 tracer->mCb.NoteNextEdgeName(buffer);
michael@0 407 } else {
michael@0 408 tracer->mCb.NoteNextEdgeName(static_cast<const char*>(tracer->debugPrintArg()));
michael@0 409 }
michael@0 410 }
michael@0 411 tracer->mCb.NoteJSChild(aThing);
michael@0 412 } else if (aTraceKind == JSTRACE_SHAPE) {
michael@0 413 JS_TraceShapeCycleCollectorChildren(aTrc, aThing);
michael@0 414 } else if (aTraceKind != JSTRACE_STRING) {
michael@0 415 JS_TraceChildren(aTrc, aThing, aTraceKind);
michael@0 416 }
michael@0 417 }
michael@0 418
michael@0 419 static void
michael@0 420 NoteJSChildTracerShim(JSTracer* aTrc, void** aThingp, JSGCTraceKind aTraceKind)
michael@0 421 {
michael@0 422 NoteJSChild(aTrc, *aThingp, aTraceKind);
michael@0 423 }
michael@0 424
michael@0 425 static void
michael@0 426 NoteJSChildGrayWrapperShim(void* aData, void* aThing)
michael@0 427 {
michael@0 428 TraversalTracer* trc = static_cast<TraversalTracer*>(aData);
michael@0 429 NoteJSChild(trc, aThing, js::GCThingTraceKind(aThing));
michael@0 430 }
michael@0 431
michael@0 432 /*
michael@0 433 * The cycle collection participant for a Zone is intended to produce the same
michael@0 434 * results as if all of the gray GCthings in a zone were merged into a single node,
michael@0 435 * except for self-edges. This avoids the overhead of representing all of the GCthings in
michael@0 436 * the zone in the cycle collector graph, which should be much faster if many of
michael@0 437 * the GCthings in the zone are gray.
michael@0 438 *
michael@0 439 * Zone merging should not always be used, because it is a conservative
michael@0 440 * approximation of the true cycle collector graph that can incorrectly identify some
michael@0 441 * garbage objects as being live. For instance, consider two cycles that pass through a
michael@0 442 * zone, where one is garbage and the other is live. If we merge the entire
michael@0 443 * zone, the cycle collector will think that both are alive.
michael@0 444 *
michael@0 445 * We don't have to worry about losing track of a garbage cycle, because any such garbage
michael@0 446 * cycle incorrectly identified as live must contain at least one C++ to JS edge, and
michael@0 447 * XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure
michael@0 448 * C++ garbage cycles, which must always be properly identified, because we clear the
michael@0 449 * purple buffer during every CC, which may contain the last reference to a garbage
michael@0 450 * cycle.)
michael@0 451 */
michael@0 452
michael@0 453 // NB: This is only used to initialize the participant in
michael@0 454 // CycleCollectedJSRuntime. It should never be used directly.
michael@0 455 static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
michael@0 456
michael@0 457 CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
michael@0 458 uint32_t aMaxbytes,
michael@0 459 JSUseHelperThreads aUseHelperThreads)
michael@0 460 : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal),
michael@0 461 mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal),
michael@0 462 mJSRuntime(nullptr),
michael@0 463 mJSHolders(512)
michael@0 464 {
michael@0 465 mozilla::dom::InitScriptSettings();
michael@0 466
michael@0 467 mJSRuntime = JS_NewRuntime(aMaxbytes, aUseHelperThreads, aParentRuntime);
michael@0 468 if (!mJSRuntime) {
michael@0 469 MOZ_CRASH();
michael@0 470 }
michael@0 471
michael@0 472 if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) {
michael@0 473 MOZ_CRASH();
michael@0 474 }
michael@0 475 JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
michael@0 476 JS_SetGCCallback(mJSRuntime, GCCallback, this);
michael@0 477 JS_SetContextCallback(mJSRuntime, ContextCallback, this);
michael@0 478 JS_SetDestroyZoneCallback(mJSRuntime, XPCStringConvert::FreeZoneCache);
michael@0 479 JS_SetSweepZoneCallback(mJSRuntime, XPCStringConvert::ClearZoneCache);
michael@0 480
michael@0 481 nsCycleCollector_registerJSRuntime(this);
michael@0 482 }
michael@0 483
michael@0 484 CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
michael@0 485 {
michael@0 486 MOZ_ASSERT(mJSRuntime);
michael@0 487 MOZ_ASSERT(!mDeferredFinalizerTable.Count());
michael@0 488 MOZ_ASSERT(!mDeferredSupports.Length());
michael@0 489
michael@0 490 // Clear mPendingException first, since it might be cycle collected.
michael@0 491 mPendingException = nullptr;
michael@0 492
michael@0 493 JS_DestroyRuntime(mJSRuntime);
michael@0 494 mJSRuntime = nullptr;
michael@0 495 nsCycleCollector_forgetJSRuntime();
michael@0 496
michael@0 497 mozilla::dom::DestroyScriptSettings();
michael@0 498 }
michael@0 499
michael@0 500 size_t
michael@0 501 CycleCollectedJSRuntime::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 502 {
michael@0 503 size_t n = 0;
michael@0 504
michael@0 505 // nullptr for the second arg; we're not measuring anything hanging off the
michael@0 506 // entries in mJSHolders.
michael@0 507 n += mJSHolders.SizeOfExcludingThis(nullptr, aMallocSizeOf);
michael@0 508
michael@0 509 return n;
michael@0 510 }
michael@0 511
michael@0 512 static PLDHashOperator
michael@0 513 UnmarkJSHolder(void* holder, nsScriptObjectTracer*& tracer, void* arg)
michael@0 514 {
michael@0 515 tracer->CanSkip(holder, true);
michael@0 516 return PL_DHASH_NEXT;
michael@0 517 }
michael@0 518
michael@0 519 void
michael@0 520 CycleCollectedJSRuntime::UnmarkSkippableJSHolders()
michael@0 521 {
michael@0 522 mJSHolders.Enumerate(UnmarkJSHolder, nullptr);
michael@0 523 }
michael@0 524
michael@0 525 void
michael@0 526 CycleCollectedJSRuntime::DescribeGCThing(bool aIsMarked, void* aThing,
michael@0 527 JSGCTraceKind aTraceKind,
michael@0 528 nsCycleCollectionTraversalCallback& aCb) const
michael@0 529 {
michael@0 530 if (!aCb.WantDebugInfo()) {
michael@0 531 aCb.DescribeGCedNode(aIsMarked, "JS Object");
michael@0 532 return;
michael@0 533 }
michael@0 534
michael@0 535 char name[72];
michael@0 536 uint64_t compartmentAddress = 0;
michael@0 537 if (aTraceKind == JSTRACE_OBJECT) {
michael@0 538 JSObject* obj = static_cast<JSObject*>(aThing);
michael@0 539 compartmentAddress = (uint64_t)js::GetObjectCompartment(obj);
michael@0 540 const js::Class* clasp = js::GetObjectClass(obj);
michael@0 541
michael@0 542 // Give the subclass a chance to do something
michael@0 543 if (DescribeCustomObjects(obj, clasp, name)) {
michael@0 544 // Nothing else to do!
michael@0 545 } else if (js::IsFunctionObject(obj)) {
michael@0 546 JSFunction* fun = JS_GetObjectFunction(obj);
michael@0 547 JSString* str = JS_GetFunctionDisplayId(fun);
michael@0 548 if (str) {
michael@0 549 NS_ConvertUTF16toUTF8 fname(JS_GetInternedStringChars(str));
michael@0 550 JS_snprintf(name, sizeof(name),
michael@0 551 "JS Object (Function - %s)", fname.get());
michael@0 552 } else {
michael@0 553 JS_snprintf(name, sizeof(name), "JS Object (Function)");
michael@0 554 }
michael@0 555 } else {
michael@0 556 JS_snprintf(name, sizeof(name), "JS Object (%s)",
michael@0 557 clasp->name);
michael@0 558 }
michael@0 559 } else {
michael@0 560 static const char trace_types[][11] = {
michael@0 561 "Object",
michael@0 562 "String",
michael@0 563 "Script",
michael@0 564 "LazyScript",
michael@0 565 "IonCode",
michael@0 566 "Shape",
michael@0 567 "BaseShape",
michael@0 568 "TypeObject",
michael@0 569 };
michael@0 570 static_assert(MOZ_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1,
michael@0 571 "JSTRACE_LAST enum must match trace_types count.");
michael@0 572 JS_snprintf(name, sizeof(name), "JS %s", trace_types[aTraceKind]);
michael@0 573 }
michael@0 574
michael@0 575 // Disable printing global for objects while we figure out ObjShrink fallout.
michael@0 576 aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
michael@0 577 }
michael@0 578
michael@0 579 void
michael@0 580 CycleCollectedJSRuntime::NoteGCThingJSChildren(void* aThing,
michael@0 581 JSGCTraceKind aTraceKind,
michael@0 582 nsCycleCollectionTraversalCallback& aCb) const
michael@0 583 {
michael@0 584 MOZ_ASSERT(mJSRuntime);
michael@0 585 TraversalTracer trc(mJSRuntime, aCb);
michael@0 586 JS_TraceChildren(&trc, aThing, aTraceKind);
michael@0 587 }
michael@0 588
michael@0 589 void
michael@0 590 CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
michael@0 591 nsCycleCollectionTraversalCallback& aCb) const
michael@0 592 {
michael@0 593 MOZ_ASSERT(aClasp);
michael@0 594 MOZ_ASSERT(aClasp == js::GetObjectClass(aObj));
michael@0 595
michael@0 596 if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) {
michael@0 597 // Nothing else to do!
michael@0 598 return;
michael@0 599 }
michael@0 600 // XXX This test does seem fragile, we should probably whitelist classes
michael@0 601 // that do hold a strong reference, but that might not be possible.
michael@0 602 else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
michael@0 603 aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
michael@0 604 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
michael@0 605 aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
michael@0 606 } else {
michael@0 607 const DOMClass* domClass = GetDOMClass(aObj);
michael@0 608 if (domClass) {
michael@0 609 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
michael@0 610 if (domClass->mDOMObjectIsISupports) {
michael@0 611 aCb.NoteXPCOMChild(UnwrapDOMObject<nsISupports>(aObj));
michael@0 612 } else if (domClass->mParticipant) {
michael@0 613 aCb.NoteNativeChild(UnwrapDOMObject<void>(aObj),
michael@0 614 domClass->mParticipant);
michael@0 615 }
michael@0 616 }
michael@0 617 }
michael@0 618 }
michael@0 619
michael@0 620 void
michael@0 621 CycleCollectedJSRuntime::TraverseGCThing(TraverseSelect aTs, void* aThing,
michael@0 622 JSGCTraceKind aTraceKind,
michael@0 623 nsCycleCollectionTraversalCallback& aCb)
michael@0 624 {
michael@0 625 MOZ_ASSERT(aTraceKind == js::GCThingTraceKind(aThing));
michael@0 626 bool isMarkedGray = xpc_IsGrayGCThing(aThing);
michael@0 627
michael@0 628 if (aTs == TRAVERSE_FULL) {
michael@0 629 DescribeGCThing(!isMarkedGray, aThing, aTraceKind, aCb);
michael@0 630 }
michael@0 631
michael@0 632 // If this object is alive, then all of its children are alive. For JS objects,
michael@0 633 // the black-gray invariant ensures the children are also marked black. For C++
michael@0 634 // objects, the ref count from this object will keep them alive. Thus we don't
michael@0 635 // need to trace our children, unless we are debugging using WantAllTraces.
michael@0 636 if (!isMarkedGray && !aCb.WantAllTraces()) {
michael@0 637 return;
michael@0 638 }
michael@0 639
michael@0 640 if (aTs == TRAVERSE_FULL) {
michael@0 641 NoteGCThingJSChildren(aThing, aTraceKind, aCb);
michael@0 642 }
michael@0 643
michael@0 644 if (aTraceKind == JSTRACE_OBJECT) {
michael@0 645 JSObject* obj = static_cast<JSObject*>(aThing);
michael@0 646 NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, aCb);
michael@0 647 }
michael@0 648 }
michael@0 649
michael@0 650 struct TraverseObjectShimClosure {
michael@0 651 nsCycleCollectionTraversalCallback& cb;
michael@0 652 CycleCollectedJSRuntime* self;
michael@0 653 };
michael@0 654
michael@0 655 void
michael@0 656 CycleCollectedJSRuntime::TraverseZone(JS::Zone* aZone,
michael@0 657 nsCycleCollectionTraversalCallback& aCb)
michael@0 658 {
michael@0 659 /*
michael@0 660 * We treat the zone as being gray. We handle non-gray GCthings in the
michael@0 661 * zone by not reporting their children to the CC. The black-gray invariant
michael@0 662 * ensures that any JS children will also be non-gray, and thus don't need to be
michael@0 663 * added to the graph. For C++ children, not representing the edge from the
michael@0 664 * non-gray JS GCthings to the C++ object will keep the child alive.
michael@0 665 *
michael@0 666 * We don't allow zone merging in a WantAllTraces CC, because then these
michael@0 667 * assumptions don't hold.
michael@0 668 */
michael@0 669 aCb.DescribeGCedNode(false, "JS Zone");
michael@0 670
michael@0 671 /*
michael@0 672 * Every JS child of everything in the zone is either in the zone
michael@0 673 * or is a cross-compartment wrapper. In the former case, we don't need to
michael@0 674 * represent these edges in the CC graph because JS objects are not ref counted.
michael@0 675 * In the latter case, the JS engine keeps a map of these wrappers, which we
michael@0 676 * iterate over. Edges between compartments in the same zone will add
michael@0 677 * unnecessary loop edges to the graph (bug 842137).
michael@0 678 */
michael@0 679 TraversalTracer trc(mJSRuntime, aCb);
michael@0 680 js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
michael@0 681
michael@0 682 /*
michael@0 683 * To find C++ children of things in the zone, we scan every JS Object in
michael@0 684 * the zone. Only JS Objects can have C++ children.
michael@0 685 */
michael@0 686 TraverseObjectShimClosure closure = { aCb, this };
michael@0 687 js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
michael@0 688 }
michael@0 689
michael@0 690 /* static */ void
michael@0 691 CycleCollectedJSRuntime::TraverseObjectShim(void* aData, void* aThing)
michael@0 692 {
michael@0 693 TraverseObjectShimClosure* closure =
michael@0 694 static_cast<TraverseObjectShimClosure*>(aData);
michael@0 695
michael@0 696 MOZ_ASSERT(js::GCThingTraceKind(aThing) == JSTRACE_OBJECT);
michael@0 697 closure->self->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP, aThing,
michael@0 698 JSTRACE_OBJECT, closure->cb);
michael@0 699 }
michael@0 700
michael@0 701 void
michael@0 702 CycleCollectedJSRuntime::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb)
michael@0 703 {
michael@0 704 // NB: This is here just to preserve the existing XPConnect order. I doubt it
michael@0 705 // would hurt to do this after the JS holders.
michael@0 706 TraverseAdditionalNativeRoots(aCb);
michael@0 707
michael@0 708 Closure closure(&aCb);
michael@0 709 mJSHolders.Enumerate(NoteJSHolder, &closure);
michael@0 710 }
michael@0 711
michael@0 712 /* static */ void
michael@0 713 CycleCollectedJSRuntime::TraceBlackJS(JSTracer* aTracer, void* aData)
michael@0 714 {
michael@0 715 CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
michael@0 716
michael@0 717 self->TraceNativeBlackRoots(aTracer);
michael@0 718 }
michael@0 719
michael@0 720 /* static */ void
michael@0 721 CycleCollectedJSRuntime::TraceGrayJS(JSTracer* aTracer, void* aData)
michael@0 722 {
michael@0 723 CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
michael@0 724
michael@0 725 // Mark these roots as gray so the CC can walk them later.
michael@0 726 self->TraceNativeGrayRoots(aTracer);
michael@0 727 }
michael@0 728
michael@0 729 /* static */ void
michael@0 730 CycleCollectedJSRuntime::GCCallback(JSRuntime* aRuntime,
michael@0 731 JSGCStatus aStatus,
michael@0 732 void* aData)
michael@0 733 {
michael@0 734 CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
michael@0 735
michael@0 736 MOZ_ASSERT(aRuntime == self->Runtime());
michael@0 737
michael@0 738 self->OnGC(aStatus);
michael@0 739 }
michael@0 740
michael@0 741 /* static */ bool
michael@0 742 CycleCollectedJSRuntime::ContextCallback(JSContext* aContext,
michael@0 743 unsigned aOperation,
michael@0 744 void* aData)
michael@0 745 {
michael@0 746 CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
michael@0 747
michael@0 748 MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime());
michael@0 749
michael@0 750 return self->CustomContextCallback(aContext, aOperation);
michael@0 751 }
michael@0 752
michael@0 753 struct JsGcTracer : public TraceCallbacks
michael@0 754 {
michael@0 755 virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 756 JS_CallHeapValueTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 757 }
michael@0 758 virtual void Trace(JS::Heap<jsid> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 759 JS_CallHeapIdTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 760 }
michael@0 761 virtual void Trace(JS::Heap<JSObject *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 762 JS_CallHeapObjectTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 763 }
michael@0 764 virtual void Trace(JS::TenuredHeap<JSObject *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 765 JS_CallTenuredObjectTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 766 }
michael@0 767 virtual void Trace(JS::Heap<JSString *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 768 JS_CallHeapStringTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 769 }
michael@0 770 virtual void Trace(JS::Heap<JSScript *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 771 JS_CallHeapScriptTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 772 }
michael@0 773 virtual void Trace(JS::Heap<JSFunction *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
michael@0 774 JS_CallHeapFunctionTracer(static_cast<JSTracer*>(closure), p, name);
michael@0 775 }
michael@0 776 };
michael@0 777
michael@0 778 static PLDHashOperator
michael@0 779 TraceJSHolder(void* aHolder, nsScriptObjectTracer*& aTracer, void* aArg)
michael@0 780 {
michael@0 781 aTracer->Trace(aHolder, JsGcTracer(), aArg);
michael@0 782
michael@0 783 return PL_DHASH_NEXT;
michael@0 784 }
michael@0 785
michael@0 786 void
michael@0 787 CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer)
michael@0 788 {
michael@0 789 // NB: This is here just to preserve the existing XPConnect order. I doubt it
michael@0 790 // would hurt to do this after the JS holders.
michael@0 791 TraceAdditionalNativeGrayRoots(aTracer);
michael@0 792
michael@0 793 mJSHolders.Enumerate(TraceJSHolder, aTracer);
michael@0 794 }
michael@0 795
michael@0 796 void
michael@0 797 CycleCollectedJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
michael@0 798 {
michael@0 799 mJSHolders.Put(aHolder, aTracer);
michael@0 800 }
michael@0 801
michael@0 802 struct ClearJSHolder : TraceCallbacks
michael@0 803 {
michael@0 804 virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 805 {
michael@0 806 *aPtr = JSVAL_VOID;
michael@0 807 }
michael@0 808
michael@0 809 virtual void Trace(JS::Heap<jsid>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 810 {
michael@0 811 *aPtr = JSID_VOID;
michael@0 812 }
michael@0 813
michael@0 814 virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 815 {
michael@0 816 *aPtr = nullptr;
michael@0 817 }
michael@0 818
michael@0 819 virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 820 {
michael@0 821 *aPtr = nullptr;
michael@0 822 }
michael@0 823
michael@0 824 virtual void Trace(JS::Heap<JSString*>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 825 {
michael@0 826 *aPtr = nullptr;
michael@0 827 }
michael@0 828
michael@0 829 virtual void Trace(JS::Heap<JSScript*>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 830 {
michael@0 831 *aPtr = nullptr;
michael@0 832 }
michael@0 833
michael@0 834 virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*, void*) const MOZ_OVERRIDE
michael@0 835 {
michael@0 836 *aPtr = nullptr;
michael@0 837 }
michael@0 838 };
michael@0 839
michael@0 840 void
michael@0 841 CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder)
michael@0 842 {
michael@0 843 nsScriptObjectTracer* tracer = mJSHolders.Get(aHolder);
michael@0 844 if (!tracer) {
michael@0 845 return;
michael@0 846 }
michael@0 847 tracer->Trace(aHolder, ClearJSHolder(), nullptr);
michael@0 848 mJSHolders.Remove(aHolder);
michael@0 849 }
michael@0 850
michael@0 851 #ifdef DEBUG
michael@0 852 bool
michael@0 853 CycleCollectedJSRuntime::IsJSHolder(void* aHolder)
michael@0 854 {
michael@0 855 return mJSHolders.Get(aHolder, nullptr);
michael@0 856 }
michael@0 857
michael@0 858 static void
michael@0 859 AssertNoGcThing(void* aGCThing, const char* aName, void* aClosure)
michael@0 860 {
michael@0 861 MOZ_ASSERT(!aGCThing);
michael@0 862 }
michael@0 863
michael@0 864 void
michael@0 865 CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
michael@0 866 {
michael@0 867 nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
michael@0 868 if (tracer) {
michael@0 869 tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
michael@0 870 }
michael@0 871 }
michael@0 872 #endif
michael@0 873
michael@0 874 already_AddRefed<nsIException>
michael@0 875 CycleCollectedJSRuntime::GetPendingException() const
michael@0 876 {
michael@0 877 nsCOMPtr<nsIException> out = mPendingException;
michael@0 878 return out.forget();
michael@0 879 }
michael@0 880
michael@0 881 void
michael@0 882 CycleCollectedJSRuntime::SetPendingException(nsIException* aException)
michael@0 883 {
michael@0 884 mPendingException = aException;
michael@0 885 }
michael@0 886
michael@0 887 nsCycleCollectionParticipant*
michael@0 888 CycleCollectedJSRuntime::GCThingParticipant()
michael@0 889 {
michael@0 890 return &mGCThingCycleCollectorGlobal;
michael@0 891 }
michael@0 892
michael@0 893 nsCycleCollectionParticipant*
michael@0 894 CycleCollectedJSRuntime::ZoneParticipant()
michael@0 895 {
michael@0 896 return &mJSZoneCycleCollectorGlobal;
michael@0 897 }
michael@0 898
michael@0 899 nsresult
michael@0 900 CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback &aCb)
michael@0 901 {
michael@0 902 TraverseNativeRoots(aCb);
michael@0 903
michael@0 904 NoteWeakMapsTracer trc(mJSRuntime, TraceWeakMapping, aCb);
michael@0 905 js::TraceWeakMaps(&trc);
michael@0 906
michael@0 907 return NS_OK;
michael@0 908 }
michael@0 909
michael@0 910 /*
michael@0 911 * Return true if there exists a JSContext with a default global whose current
michael@0 912 * inner is gray. The intent is to look for JS Object windows. We don't merge
michael@0 913 * system compartments, so we don't use them to trigger merging CCs.
michael@0 914 */
michael@0 915 bool
michael@0 916 CycleCollectedJSRuntime::UsefulToMergeZones() const
michael@0 917 {
michael@0 918 if (!NS_IsMainThread()) {
michael@0 919 return false;
michael@0 920 }
michael@0 921
michael@0 922 JSContext* iter = nullptr;
michael@0 923 JSContext* cx;
michael@0 924 JSAutoRequest ar(nsContentUtils::GetSafeJSContext());
michael@0 925 while ((cx = JS_ContextIterator(mJSRuntime, &iter))) {
michael@0 926 // Skip anything without an nsIScriptContext.
michael@0 927 nsIScriptContext* scx = GetScriptContextFromJSContext(cx);
michael@0 928 JS::RootedObject obj(cx, scx ? scx->GetWindowProxyPreserveColor() : nullptr);
michael@0 929 if (!obj) {
michael@0 930 continue;
michael@0 931 }
michael@0 932 MOZ_ASSERT(js::IsOuterObject(obj));
michael@0 933 // Grab the inner from the outer.
michael@0 934 obj = JS_ObjectToInnerObject(cx, obj);
michael@0 935 MOZ_ASSERT(!js::GetObjectParent(obj));
michael@0 936 if (JS::GCThingIsMarkedGray(obj) &&
michael@0 937 !js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
michael@0 938 return true;
michael@0 939 }
michael@0 940 }
michael@0 941 return false;
michael@0 942 }
michael@0 943
michael@0 944 void
michael@0 945 CycleCollectedJSRuntime::FixWeakMappingGrayBits() const
michael@0 946 {
michael@0 947 FixWeakMappingGrayBitsTracer fixer(mJSRuntime);
michael@0 948 fixer.FixAll();
michael@0 949 }
michael@0 950
michael@0 951 bool
michael@0 952 CycleCollectedJSRuntime::NeedCollect() const
michael@0 953 {
michael@0 954 return !js::AreGCGrayBitsValid(mJSRuntime);
michael@0 955 }
michael@0 956
michael@0 957 void
michael@0 958 CycleCollectedJSRuntime::Collect(uint32_t aReason) const
michael@0 959 {
michael@0 960 MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
michael@0 961 JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
michael@0 962
michael@0 963 JS::PrepareForFullGC(mJSRuntime);
michael@0 964 JS::GCForReason(mJSRuntime, gcreason);
michael@0 965 }
michael@0 966
michael@0 967 void
michael@0 968 CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
michael@0 969 DeferredFinalizeFunction aFunc,
michael@0 970 void* aThing)
michael@0 971 {
michael@0 972 void* thingArray = nullptr;
michael@0 973 bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
michael@0 974
michael@0 975 thingArray = aAppendFunc(thingArray, aThing);
michael@0 976 if (!hadThingArray) {
michael@0 977 mDeferredFinalizerTable.Put(aFunc, thingArray);
michael@0 978 }
michael@0 979 }
michael@0 980
michael@0 981 void
michael@0 982 CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
michael@0 983 {
michael@0 984 mDeferredSupports.AppendElement(aSupports);
michael@0 985 }
michael@0 986
michael@0 987 void
michael@0 988 CycleCollectedJSRuntime::DumpJSHeap(FILE* file)
michael@0 989 {
michael@0 990 js::DumpHeapComplete(Runtime(), file, js::CollectNurseryBeforeDump);
michael@0 991 }
michael@0 992
michael@0 993
michael@0 994 bool
michael@0 995 ReleaseSliceNow(uint32_t aSlice, void* aData)
michael@0 996 {
michael@0 997 MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with slice == 0");
michael@0 998 nsTArray<nsISupports*>* items = static_cast<nsTArray<nsISupports*>*>(aData);
michael@0 999
michael@0 1000 uint32_t length = items->Length();
michael@0 1001 aSlice = std::min(aSlice, length);
michael@0 1002 for (uint32_t i = length; i > length - aSlice; --i) {
michael@0 1003 // Remove (and NS_RELEASE) the last entry in "items":
michael@0 1004 uint32_t lastItemIdx = i - 1;
michael@0 1005
michael@0 1006 nsISupports* wrapper = items->ElementAt(lastItemIdx);
michael@0 1007 items->RemoveElementAt(lastItemIdx);
michael@0 1008 NS_IF_RELEASE(wrapper);
michael@0 1009 }
michael@0 1010
michael@0 1011 return items->IsEmpty();
michael@0 1012 }
michael@0 1013
michael@0 1014 /* static */ PLDHashOperator
michael@0 1015 IncrementalFinalizeRunnable::DeferredFinalizerEnumerator(DeferredFinalizeFunction& aFunction,
michael@0 1016 void*& aData,
michael@0 1017 void* aClosure)
michael@0 1018 {
michael@0 1019 DeferredFinalizeArray* array = static_cast<DeferredFinalizeArray*>(aClosure);
michael@0 1020
michael@0 1021 DeferredFinalizeFunctionHolder* function = array->AppendElement();
michael@0 1022 function->run = aFunction;
michael@0 1023 function->data = aData;
michael@0 1024
michael@0 1025 return PL_DHASH_REMOVE;
michael@0 1026 }
michael@0 1027
michael@0 1028 IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
michael@0 1029 nsTArray<nsISupports*>& aSupports,
michael@0 1030 DeferredFinalizerTable& aFinalizers)
michael@0 1031 : mRuntime(aRt),
michael@0 1032 mFinalizeFunctionToRun(0)
michael@0 1033 {
michael@0 1034 this->mSupports.SwapElements(aSupports);
michael@0 1035 DeferredFinalizeFunctionHolder* function = mDeferredFinalizeFunctions.AppendElement();
michael@0 1036 function->run = ReleaseSliceNow;
michael@0 1037 function->data = &this->mSupports;
michael@0 1038
michael@0 1039 // Enumerate the hashtable into our array.
michael@0 1040 aFinalizers.Enumerate(DeferredFinalizerEnumerator, &mDeferredFinalizeFunctions);
michael@0 1041 }
michael@0 1042
michael@0 1043 IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
michael@0 1044 {
michael@0 1045 MOZ_ASSERT(this != mRuntime->mFinalizeRunnable);
michael@0 1046 }
michael@0 1047
michael@0 1048 void
michael@0 1049 IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
michael@0 1050 {
michael@0 1051 //MOZ_ASSERT(NS_IsMainThread());
michael@0 1052 MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
michael@0 1053 "We should have at least ReleaseSliceNow to run");
michael@0 1054 MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
michael@0 1055 "No more finalizers to run?");
michael@0 1056
michael@0 1057 TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
michael@0 1058 TimeStamp started = TimeStamp::Now();
michael@0 1059 bool timeout = false;
michael@0 1060 do {
michael@0 1061 const DeferredFinalizeFunctionHolder &function =
michael@0 1062 mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
michael@0 1063 if (aLimited) {
michael@0 1064 bool done = false;
michael@0 1065 while (!timeout && !done) {
michael@0 1066 /*
michael@0 1067 * We don't want to read the clock too often, so we try to
michael@0 1068 * release slices of 100 items.
michael@0 1069 */
michael@0 1070 done = function.run(100, function.data);
michael@0 1071 timeout = TimeStamp::Now() - started >= sliceTime;
michael@0 1072 }
michael@0 1073 if (done) {
michael@0 1074 ++mFinalizeFunctionToRun;
michael@0 1075 }
michael@0 1076 if (timeout) {
michael@0 1077 break;
michael@0 1078 }
michael@0 1079 } else {
michael@0 1080 function.run(UINT32_MAX, function.data);
michael@0 1081 ++mFinalizeFunctionToRun;
michael@0 1082 }
michael@0 1083 } while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
michael@0 1084
michael@0 1085 if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
michael@0 1086 MOZ_ASSERT(mRuntime->mFinalizeRunnable == this);
michael@0 1087 mDeferredFinalizeFunctions.Clear();
michael@0 1088 // NB: This may delete this!
michael@0 1089 mRuntime->mFinalizeRunnable = nullptr;
michael@0 1090 }
michael@0 1091 }
michael@0 1092
michael@0 1093 NS_IMETHODIMP
michael@0 1094 IncrementalFinalizeRunnable::Run()
michael@0 1095 {
michael@0 1096 if (mRuntime->mFinalizeRunnable != this) {
michael@0 1097 /* These items were already processed synchronously in JSGC_END. */
michael@0 1098 MOZ_ASSERT(!mSupports.Length());
michael@0 1099 MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
michael@0 1100 return NS_OK;
michael@0 1101 }
michael@0 1102
michael@0 1103 ReleaseNow(true);
michael@0 1104
michael@0 1105 if (mDeferredFinalizeFunctions.Length()) {
michael@0 1106 nsresult rv = NS_DispatchToCurrentThread(this);
michael@0 1107 if (NS_FAILED(rv)) {
michael@0 1108 ReleaseNow(false);
michael@0 1109 }
michael@0 1110 }
michael@0 1111
michael@0 1112 return NS_OK;
michael@0 1113 }
michael@0 1114
michael@0 1115 void
michael@0 1116 CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
michael@0 1117 {
michael@0 1118 MOZ_ASSERT(!mFinalizeRunnable);
michael@0 1119 mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
michael@0 1120 mDeferredSupports,
michael@0 1121 mDeferredFinalizerTable);
michael@0 1122
michael@0 1123 // Everything should be gone now.
michael@0 1124 MOZ_ASSERT(!mDeferredSupports.Length());
michael@0 1125 MOZ_ASSERT(!mDeferredFinalizerTable.Count());
michael@0 1126
michael@0 1127 if (aType == FinalizeIncrementally) {
michael@0 1128 NS_DispatchToCurrentThread(mFinalizeRunnable);
michael@0 1129 } else {
michael@0 1130 mFinalizeRunnable->ReleaseNow(false);
michael@0 1131 MOZ_ASSERT(!mFinalizeRunnable);
michael@0 1132 }
michael@0 1133 }
michael@0 1134
michael@0 1135 void
michael@0 1136 CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
michael@0 1137 {
michael@0 1138 switch (aStatus) {
michael@0 1139 case JSGC_BEGIN:
michael@0 1140 nsCycleCollector_prepareForGarbageCollection();
michael@0 1141 break;
michael@0 1142 case JSGC_END:
michael@0 1143 {
michael@0 1144 /*
michael@0 1145 * If the previous GC created a runnable to finalize objects
michael@0 1146 * incrementally, and if it hasn't finished yet, finish it now. We
michael@0 1147 * don't want these to build up. We also don't want to allow any
michael@0 1148 * existing incremental finalize runnables to run after a
michael@0 1149 * non-incremental GC, since they are often used to detect leaks.
michael@0 1150 */
michael@0 1151 if (mFinalizeRunnable) {
michael@0 1152 mFinalizeRunnable->ReleaseNow(false);
michael@0 1153 }
michael@0 1154
michael@0 1155 // Do any deferred finalization of native objects.
michael@0 1156 FinalizeDeferredThings(JS::WasIncrementalGC(mJSRuntime) ? FinalizeIncrementally :
michael@0 1157 FinalizeNow);
michael@0 1158 break;
michael@0 1159 }
michael@0 1160 default:
michael@0 1161 MOZ_CRASH();
michael@0 1162 }
michael@0 1163
michael@0 1164 CustomGCCallback(aStatus);
michael@0 1165 }

mercurial