From f192791e296d7e20dd6adebd9d7d70e27204d675 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Sun, 27 Jan 2013 12:35:12 -0800 Subject: [PATCH] Bug 751618 - Add js/GCAPI.h (r=terrence) --- dom/base/nsIScriptContext.h | 3 +- dom/base/nsJSEnvironment.cpp | 66 ++--- dom/base/nsJSEnvironment.h | 6 +- dom/workers/EventListenerManager.cpp | 5 +- js/jsd/jsd_xpc.cpp | 12 +- js/public/GCAPI.h | 244 +++++++++++++++++++ js/public/HeapAPI.h | 26 -- js/src/Makefile.in | 1 + js/src/gc/Marking.cpp | 2 +- js/src/gc/Statistics.h | 2 + js/src/jsfriendapi.cpp | 46 ++-- js/src/jsfriendapi.h | 204 ---------------- js/src/jsgc.cpp | 4 +- js/src/jsgc.h | 3 - js/xpconnect/src/nsXPConnect.cpp | 4 +- js/xpconnect/src/xpcpublic.h | 1 + toolkit/components/telemetry/Histograms.json | 2 +- toolkit/components/telemetry/Telemetry.cpp | 1 + xpcom/base/nsCycleCollector.cpp | 2 +- 19 files changed, 324 insertions(+), 310 deletions(-) create mode 100644 js/public/GCAPI.h diff --git a/dom/base/nsIScriptContext.h b/dom/base/nsIScriptContext.h index ae57f5104385..f3b47077dcf5 100644 --- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -13,6 +13,7 @@ #include "nsIProgrammingLanguage.h" #include "jsfriendapi.h" #include "jspubtd.h" +#include "js/GCAPI.h" class nsIScriptGlobalObject; class nsIScriptSecurityManager; @@ -201,7 +202,7 @@ public: * * @return NS_OK if the method is successful */ - virtual void GC(js::gcreason::Reason aReason) = 0; + virtual void GC(JS::gcreason::Reason aReason) = 0; /** * Inform the context that a script was evaluated. diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 377132b881f6..f8c9984dbf44 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -146,7 +146,7 @@ static PRTime sLastCCEndTime; static bool sCCLockedOut; static PRTime sCCLockedOutTime; -static js::GCSliceCallback sPrevGCSliceCallback; +static JS::GCSliceCallback sPrevGCSliceCallback; static js::AnalysisPurgeCallback sPrevAnalysisPurgeCallback; // The number of currently pending document loads. This count isn't @@ -235,7 +235,7 @@ nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) { - nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, + nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE, nsJSContext::NonIncrementalGC, nsJSContext::NonCompartmentGC, nsJSContext::ShrinkingGC); @@ -1181,7 +1181,7 @@ nsJSContext::DestroyJSContext() js_options_dot_str, this); if (mGCOnDestruction) { - PokeGC(js::gcreason::NSJSCONTEXT_DESTROY); + PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY); } // Let xpconnect destroy the JSContext when it thinks the time is right. @@ -2536,13 +2536,13 @@ FullGCTimerFired(nsITimer* aTimer, void* aClosure) NS_RELEASE(sFullGCTimer); uintptr_t reason = reinterpret_cast(aClosure); - nsJSContext::GarbageCollectNow(static_cast(reason), + nsJSContext::GarbageCollectNow(static_cast(reason), nsJSContext::IncrementalGC); } //static void -nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, +nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason, IsIncremental aIncremental, IsCompartment aCompartment, IsShrinking aShrinking, @@ -2570,8 +2570,8 @@ nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, if (sCCLockedOut && aIncremental == IncrementalGC) { // We're in the middle of incremental GC. Do another slice. - js::PrepareForIncrementalGC(nsJSRuntime::sRuntime); - js::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis); + JS::PrepareForIncrementalGC(nsJSRuntime::sRuntime); + JS::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis); return; } @@ -2581,20 +2581,20 @@ nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, if (!sDisableExplicitCompartmentGC && aShrinking != ShrinkingGC && aCompartment != NonCompartmentGC && sCompartmentGCCount < NS_MAX_COMPARTMENT_GC_COUNT) { - js::PrepareForFullGC(nsJSRuntime::sRuntime); + JS::PrepareForFullGC(nsJSRuntime::sRuntime); for (nsJSContext* cx = sContextList; cx; cx = cx->mNext) { if (!cx->mActive && cx->mContext) { if (JSObject* global = cx->GetNativeGlobal()) { - js::SkipCompartmentForGC(js::GetObjectCompartment(global)); + JS::SkipCompartmentForGC(js::GetObjectCompartment(global)); } } cx->mActive = false; } - if (js::IsGCScheduled(nsJSRuntime::sRuntime)) { + if (JS::IsGCScheduled(nsJSRuntime::sRuntime)) { if (aIncremental == IncrementalGC) { - js::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis); + JS::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis); } else { - js::GCForReason(nsJSRuntime::sRuntime, aReason); + JS::GCForReason(nsJSRuntime::sRuntime, aReason); } } return; @@ -2603,11 +2603,11 @@ nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, for (nsJSContext* cx = sContextList; cx; cx = cx->mNext) { cx->mActive = false; } - js::PrepareForFullGC(nsJSRuntime::sRuntime); + JS::PrepareForFullGC(nsJSRuntime::sRuntime); if (aIncremental == IncrementalGC) { - js::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis); + JS::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis); } else { - js::GCForReason(nsJSRuntime::sRuntime, aReason); + JS::GCForReason(nsJSRuntime::sRuntime, aReason); } } @@ -2619,7 +2619,7 @@ nsJSContext::ShrinkGCBuffersNow() KillShrinkGCBuffersTimer(); - JS_ShrinkGCBuffers(nsJSRuntime::sRuntime); + JS::ShrinkGCBuffers(nsJSRuntime::sRuntime); } // Return true if any JSContext has a "global object" with a gray @@ -2636,7 +2636,7 @@ AnyGrayGlobalParent() while ((cx = JS_ContextIterator(nsJSRuntime::sRuntime, &iter))) { if (JSObject *global = JS_GetGlobalObject(cx)) { if (JSObject *parent = js::GetObjectParent(global)) { - if (js::GCThingIsMarkedGray(parent) && + if (JS::GCThingIsMarkedGray(parent) && !js::IsSystemCompartment(js::GetGCThingCompartment(parent))) { return true; } @@ -2686,8 +2686,8 @@ FinishAnyIncrementalGC() { if (sCCLockedOut) { // We're in the middle of an incremental GC, so finish it. - js::PrepareForIncrementalGC(nsJSRuntime::sRuntime); - js::FinishIncrementalGC(nsJSRuntime::sRuntime, js::gcreason::CC_FORCED); + JS::PrepareForIncrementalGC(nsJSRuntime::sRuntime); + JS::FinishIncrementalGC(nsJSRuntime::sRuntime, JS::gcreason::CC_FORCED); } } @@ -2770,7 +2770,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, // If we collected a substantial amount of cycles, poke the GC since more objects // might be unreachable now. if (sCCollectedWaitingForGC > 250) { - PokeGC(js::gcreason::CC_WAITING); + PokeGC(JS::gcreason::CC_WAITING); } PRTime endCCTime = PR_Now(); @@ -2888,7 +2888,7 @@ void InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure) { NS_RELEASE(sInterSliceGCTimer); - nsJSContext::GarbageCollectNow(js::gcreason::INTER_SLICE_GC, + nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC, nsJSContext::IncrementalGC, nsJSContext::CompartmentGC, nsJSContext::NonShrinkingGC, @@ -2902,7 +2902,7 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) NS_RELEASE(sGCTimer); uintptr_t reason = reinterpret_cast(aClosure); - nsJSContext::GarbageCollectNow(static_cast(reason), + nsJSContext::GarbageCollectNow(static_cast(reason), nsJSContext::IncrementalGC, nsJSContext::CompartmentGC); } @@ -3019,12 +3019,12 @@ nsJSContext::LoadEnd() // Its probably a good idea to GC soon since we have finished loading. sLoadingInProgress = false; - PokeGC(js::gcreason::LOAD_END); + PokeGC(JS::gcreason::LOAD_END); } // static void -nsJSContext::PokeGC(js::gcreason::Reason aReason, int aDelay) +nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay) { if (sGCTimer || sShuttingDown) { // There's already a timer for GC'ing, just return @@ -3145,7 +3145,7 @@ nsJSContext::KillCCTimer() } void -nsJSContext::GC(js::gcreason::Reason aReason) +nsJSContext::GC(JS::gcreason::Reason aReason) { mActive = true; PokeGC(aReason); @@ -3179,11 +3179,11 @@ NotifyGCEndRunnable::Run() } static void -DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescription &aDesc) +DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc) { NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread"); - if (aProgress == js::GC_CYCLE_END) { + if (aProgress == JS::GC_CYCLE_END) { PRTime delta = GetCollectionTimeDelta(); if (sPostGCEventsToConsole) { @@ -3208,15 +3208,15 @@ DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescrip } // Prevent cycle collections and shrinking during incremental GC. - if (aProgress == js::GC_CYCLE_BEGIN) { + if (aProgress == JS::GC_CYCLE_BEGIN) { sCCLockedOut = true; nsJSContext::KillShrinkGCBuffersTimer(); - } else if (aProgress == js::GC_CYCLE_END) { + } else if (aProgress == JS::GC_CYCLE_END) { sCCLockedOut = false; } // The GC has more work to do, so schedule another GC slice. - if (aProgress == js::GC_SLICE_END) { + if (aProgress == JS::GC_SLICE_END) { nsJSContext::KillInterSliceGCTimer(); if (!sShuttingDown) { CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer); @@ -3227,7 +3227,7 @@ DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescrip } } - if (aProgress == js::GC_CYCLE_END) { + if (aProgress == JS::GC_CYCLE_END) { // May need to kill the inter-slice GC timer nsJSContext::KillInterSliceGCTimer(); @@ -3240,7 +3240,7 @@ DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescrip ++sCompartmentGCCount; if (!sFullGCTimer && !sShuttingDown) { CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer); - js::gcreason::Reason reason = js::gcreason::FULL_GC_TIMER; + JS::gcreason::Reason reason = JS::gcreason::FULL_GC_TIMER; sFullGCTimer->InitWithFuncCallback(FullGCTimerFired, reinterpret_cast(reason), NS_FULL_GC_DELAY, @@ -3567,7 +3567,7 @@ nsJSRuntime::Init() // Let's make sure that our main thread is the same as the xpcom main thread. NS_ASSERTION(NS_IsMainThread(), "bad"); - sPrevGCSliceCallback = js::SetGCSliceCallback(sRuntime, DOMGCSliceCallback); + sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback); sPrevAnalysisPurgeCallback = js::SetAnalysisPurgeCallback(sRuntime, DOMAnalysisPurgeCallback); // Set up the structured clone callbacks. diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index d73e1c7c83cc..17c34c6b55c2 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -125,7 +125,7 @@ public: NonIncrementalGC }; - static void GarbageCollectNow(js::gcreason::Reason reason, + static void GarbageCollectNow(JS::gcreason::Reason reason, IsIncremental aIncremental = NonIncrementalGC, IsCompartment aCompartment = NonCompartmentGC, IsShrinking aShrinking = NonShrinkingGC, @@ -137,7 +137,7 @@ public: int32_t aExtraForgetSkippableCalls = 0, bool aForced = true); - static void PokeGC(js::gcreason::Reason aReason, int aDelay = 0); + static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0); static void KillGCTimer(); static void PokeShrinkGCBuffers(); @@ -148,7 +148,7 @@ public: static void KillFullGCTimer(); static void KillInterSliceGCTimer(); - virtual void GC(js::gcreason::Reason aReason); + virtual void GC(JS::gcreason::Reason aReason); static uint32_t CleanupsSinceLastGC(); diff --git a/dom/workers/EventListenerManager.cpp b/dom/workers/EventListenerManager.cpp index 64a709f06a26..5984ad187942 100644 --- a/dom/workers/EventListenerManager.cpp +++ b/dom/workers/EventListenerManager.cpp @@ -8,6 +8,7 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "js/Vector.h" +#include "js/GCAPI.h" #include "mozilla/Util.h" #include "nsAutoJSValHolder.h" @@ -77,8 +78,8 @@ struct ListenerData : PRCList static void Remove(JSContext* aCx, ListenerData* aListenerData) { - if (js::IsIncrementalBarrierNeeded(aCx)) { - js:: IncrementalReferenceBarrier(aListenerData->mListener); + if (JS::IsIncrementalBarrierNeeded(aCx)) { + JS:: IncrementalReferenceBarrier(aListenerData->mListener); } PR_REMOVE_LINK(aListenerData); diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 6ce68cddfa76..d48fcde05fdb 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -8,6 +8,8 @@ #include "jslock.h" #include "jsd_xpc.h" +#include "js/GCAPI.h" + #include "nsIXPConnect.h" #include "mozilla/ModuleUtils.h" #include "nsIServiceManager.h" @@ -77,7 +79,7 @@ #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer" static void -jsds_GCSliceCallbackProc (JSRuntime *rt, js::GCProgress progress, const js::GCDescription &desc); +jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc); /******************************************************************************* * global vars @@ -98,7 +100,7 @@ uint32_t gFrameCount = 0; #endif static jsdService *gJsds = 0; -static js::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc; +static JS::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc; static bool gGCRunning = false; static struct DeadScript { @@ -478,9 +480,9 @@ jsds_NotifyPendingDeadScripts (JSRuntime *rt) } static void -jsds_GCSliceCallbackProc (JSRuntime *rt, js::GCProgress progress, const js::GCDescription &desc) +jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc) { - if (progress == js::GC_CYCLE_END || progress == js::GC_SLICE_END) { + if (progress == JS::GC_CYCLE_END || progress == JS::GC_SLICE_END) { NS_ASSERTION(gGCRunning, "GC slice callback was missed"); while (gDeadScripts) @@ -2556,7 +2558,7 @@ jsdService::ActivateDebugger (JSRuntime *rt) if (gPrevGCSliceCallback == jsds_GCSliceCallbackProc) /* condition indicates that the callback proc has not been set yet */ - gPrevGCSliceCallback = js::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc); + gPrevGCSliceCallback = JS::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc); mCx = JSD_DebuggerOnForUser (rt, NULL, NULL); if (!mCx) diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h new file mode 100644 index 000000000000..279f465c190e --- /dev/null +++ b/js/public/GCAPI.h @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_gc_api_h___ +#define js_gc_api_h___ + +#include "HeapAPI.h" + +namespace JS { + +#define GCREASONS(D) \ + /* Reasons internal to the JS engine */ \ + D(API) \ + D(MAYBEGC) \ + D(LAST_CONTEXT) \ + D(DESTROY_CONTEXT) \ + D(LAST_DITCH) \ + D(TOO_MUCH_MALLOC) \ + D(ALLOC_TRIGGER) \ + D(DEBUG_GC) \ + D(DEBUG_MODE_GC) \ + D(TRANSPLANT) \ + D(RESET) \ + \ + /* Reasons from Firefox */ \ + D(DOM_WINDOW_UTILS) \ + D(COMPONENT_UTILS) \ + D(MEM_PRESSURE) \ + D(CC_WAITING) \ + D(CC_FORCED) \ + D(LOAD_END) \ + D(POST_COMPARTMENT) \ + D(PAGE_HIDE) \ + D(NSJSCONTEXT_DESTROY) \ + D(SET_NEW_DOCUMENT) \ + D(SET_DOC_SHELL) \ + D(DOM_UTILS) \ + D(DOM_IPC) \ + D(DOM_WORKER) \ + D(INTER_SLICE_GC) \ + D(REFRESH_FRAME) \ + D(FULL_GC_TIMER) \ + D(SHUTDOWN_CC) + +namespace gcreason { + +/* GCReasons will end up looking like JSGC_MAYBEGC */ +enum Reason { +#define MAKE_REASON(name) name, + GCREASONS(MAKE_REASON) +#undef MAKE_REASON + NO_REASON, + NUM_REASONS, + + /* + * For telemetry, we want to keep a fixed max bucket size over time so we + * don't have to switch histograms. 100 is conservative; as of this writing + * there are 26. But the cost of extra buckets seems to be low while the + * cost of switching histograms is high. + */ + NUM_TELEMETRY_REASONS = 100 +}; + +} /* namespace gcreason */ + +extern JS_FRIEND_API(void) +PrepareCompartmentForGC(JSCompartment *comp); + +extern JS_FRIEND_API(void) +PrepareForFullGC(JSRuntime *rt); + +extern JS_FRIEND_API(void) +PrepareForIncrementalGC(JSRuntime *rt); + +extern JS_FRIEND_API(bool) +IsGCScheduled(JSRuntime *rt); + +extern JS_FRIEND_API(void) +SkipCompartmentForGC(JSCompartment *comp); + +/* + * When triggering a GC using one of the functions below, it is first necessary + * to select the compartments to be collected. To do this, you can call + * PrepareCompartmentForGC on each compartment, or you can call PrepareForFullGC + * to select all compartments. Failing to select any compartment is an error. + */ + +extern JS_FRIEND_API(void) +GCForReason(JSRuntime *rt, gcreason::Reason reason); + +extern JS_FRIEND_API(void) +ShrinkingGC(JSRuntime *rt, gcreason::Reason reason); + +extern JS_FRIEND_API(void) +ShrinkGCBuffers(JSRuntime *rt); + +extern JS_FRIEND_API(void) +IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0); + +extern JS_FRIEND_API(void) +FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason); + +enum GCProgress { + /* + * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END + * callbacks. During an incremental GC, the sequence of callbacks is as + * follows: + * JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice) + * JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice) + * ... + * JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice) + */ + + GC_CYCLE_BEGIN, + GC_SLICE_BEGIN, + GC_SLICE_END, + GC_CYCLE_END +}; + +struct JS_FRIEND_API(GCDescription) { + bool isCompartment; + + GCDescription(bool isCompartment) + : isCompartment(isCompartment) {} + + jschar *formatMessage(JSRuntime *rt) const; + jschar *formatJSON(JSRuntime *rt, uint64_t timestamp) const; +}; + +typedef void +(* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc); + +extern JS_FRIEND_API(GCSliceCallback) +SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback); + +/* + * Signals a good place to do an incremental slice, because the browser is + * drawing a frame. + */ +extern JS_FRIEND_API(void) +NotifyDidPaint(JSRuntime *rt); + +extern JS_FRIEND_API(bool) +IsIncrementalGCEnabled(JSRuntime *rt); + +JS_FRIEND_API(bool) +IsIncrementalGCInProgress(JSRuntime *rt); + +extern JS_FRIEND_API(void) +DisableIncrementalGC(JSRuntime *rt); + +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSRuntime *rt); + +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSContext *cx); + +extern JS_FRIEND_API(void) +IncrementalReferenceBarrier(void *ptr); + +extern JS_FRIEND_API(void) +IncrementalValueBarrier(const Value &v); + +extern JS_FRIEND_API(void) +PokeGC(JSRuntime *rt); + +/* Was the most recent GC run incrementally? */ +extern JS_FRIEND_API(bool) +WasIncrementalGC(JSRuntime *rt); + +class ObjectPtr +{ + JSObject *value; + + public: + ObjectPtr() : value(NULL) {} + + ObjectPtr(JSObject *obj) : value(obj) {} + + /* Always call finalize before the destructor. */ + ~ObjectPtr() { JS_ASSERT(!value); } + + void finalize(JSRuntime *rt) { + if (IsIncrementalBarrierNeeded(rt)) + IncrementalReferenceBarrier(value); + value = NULL; + } + + void init(JSObject *obj) { value = obj; } + + JSObject *get() const { return value; } + + void writeBarrierPre(JSRuntime *rt) { + IncrementalReferenceBarrier(value); + } + + ObjectPtr &operator=(JSObject *obj) { + IncrementalReferenceBarrier(value); + value = obj; + return *this; + } + + JSObject &operator*() const { return *value; } + JSObject *operator->() const { return value; } + operator JSObject *() const { return value; } +}; + +/* + * Unsets the gray bit for anything reachable from |thing|. |kind| should not be + * JSTRACE_SHAPE. |thing| should be non-null. + */ +extern JS_FRIEND_API(void) +UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind); + +/* + * This should be called when an object that is marked gray is exposed to the JS + * engine (by handing it to running JS code or writing it into live JS + * data). During incremental GC, since the gray bits haven't been computed yet, + * we conservatively mark the object black. + */ +static JS_ALWAYS_INLINE void +ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind) +{ + JS_ASSERT(kind != JSTRACE_SHAPE); + + if (GCThingIsMarkedGray(thing)) + UnmarkGrayGCThingRecursively(thing, kind); + else if (IsIncrementalBarrierNeededOnGCThing(thing, kind)) + IncrementalReferenceBarrier(thing); +} + +static JS_ALWAYS_INLINE void +ExposeValueToActiveJS(const Value &v) +{ + if (v.isMarkable()) + ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind()); +} + +} /* namespace JS */ + +#endif /* js_gc_api_h___ */ diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h index ca1a59608792..febca9dc1717 100644 --- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -7,8 +7,6 @@ #ifndef js_heap_api_h___ #define js_heap_api_h___ -#include "jsfriendapi.h" - /* These values are private to the JS engine. */ namespace js { namespace gc { @@ -140,30 +138,6 @@ IsIncrementalBarrierNeededOnGCThing(void *thing, JSGCTraceKind kind) return reinterpret_cast(comp)->needsBarrier_; } -/* - * This should be called when an object that is marked gray is exposed to the JS - * engine (by handing it to running JS code or writing it into live JS - * data). During incremental GC, since the gray bits haven't been computed yet, - * we conservatively mark the object black. - */ -static JS_ALWAYS_INLINE void -ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind) -{ - JS_ASSERT(kind != JSTRACE_SHAPE); - - if (GCThingIsMarkedGray(thing)) - js::UnmarkGrayGCThingRecursively(thing, kind); - else if (IsIncrementalBarrierNeededOnGCThing(thing, kind)) - js::IncrementalReferenceBarrier(thing); -} - -static JS_ALWAYS_INLINE void -ExposeValueToActiveJS(const Value &v) -{ - if (v.isMarkable()) - ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind()); -} - } /* namespace JS */ #endif /* js_heap_api_h___ */ diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 049529eff797..7c0ab9602b96 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -228,6 +228,7 @@ EXPORTS_js = \ CharacterEncoding.h \ HashTable.h \ HeapAPI.h \ + GCAPI.h \ LegacyIntTypes.h \ MemoryMetrics.h \ TemplateLib.h \ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index a3c01e795499..e41375b29c0c 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1660,7 +1660,7 @@ UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind) } JS_FRIEND_API(void) -js::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind) +JS::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind) { JS_ASSERT(kind != JSTRACE_SHAPE); diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index b447a947249f..9efd6e649cb8 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -17,6 +17,8 @@ #include "jspubtd.h" #include "jsutil.h" +#include "js/GCAPI.h" + struct JSCompartment; namespace js { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index e31eaca36820..6f98c3986bcb 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -127,20 +127,20 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *protoArg, JS } JS_FRIEND_API(void) -js::PrepareCompartmentForGC(JSCompartment *comp) +JS::PrepareCompartmentForGC(JSCompartment *comp) { comp->scheduleGC(); } JS_FRIEND_API(void) -js::PrepareForFullGC(JSRuntime *rt) +JS::PrepareForFullGC(JSRuntime *rt) { for (CompartmentsIter c(rt); !c.done(); c.next()) c->scheduleGC(); } JS_FRIEND_API(void) -js::PrepareForIncrementalGC(JSRuntime *rt) +JS::PrepareForIncrementalGC(JSRuntime *rt) { if (!IsIncrementalGCInProgress(rt)) return; @@ -152,7 +152,7 @@ js::PrepareForIncrementalGC(JSRuntime *rt) } JS_FRIEND_API(bool) -js::IsGCScheduled(JSRuntime *rt) +JS::IsGCScheduled(JSRuntime *rt) { for (CompartmentsIter c(rt); !c.done(); c.next()) { if (c->isGCScheduled()) @@ -163,41 +163,35 @@ js::IsGCScheduled(JSRuntime *rt) } JS_FRIEND_API(void) -js::SkipCompartmentForGC(JSCompartment *comp) +JS::SkipCompartmentForGC(JSCompartment *comp) { comp->unscheduleGC(); } JS_FRIEND_API(void) -js::GCForReason(JSRuntime *rt, gcreason::Reason reason) +JS::GCForReason(JSRuntime *rt, gcreason::Reason reason) { GC(rt, GC_NORMAL, reason); } JS_FRIEND_API(void) -js::ShrinkingGC(JSRuntime *rt, gcreason::Reason reason) +JS::ShrinkingGC(JSRuntime *rt, gcreason::Reason reason) { GC(rt, GC_SHRINK, reason); } JS_FRIEND_API(void) -js::IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis) +JS::IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis) { GCSlice(rt, GC_NORMAL, reason, millis); } JS_FRIEND_API(void) -js::FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason) +JS::FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason) { GCFinalSlice(rt, GC_NORMAL, reason); } -JS_FRIEND_API(void) -JS_ShrinkGCBuffers(JSRuntime *rt) -{ - ShrinkGCBuffers(rt); -} - JS_FRIEND_API(JSPrincipals *) JS_GetCompartmentPrincipals(JSCompartment *compartment) { @@ -786,7 +780,7 @@ js::GetRuntimeCompartments(JSRuntime *rt) } JS_FRIEND_API(GCSliceCallback) -js::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) +JS::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) { GCSliceCallback old = rt->gcSliceCallback; rt->gcSliceCallback = callback; @@ -794,7 +788,7 @@ js::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) } JS_FRIEND_API(bool) -js::WasIncrementalGC(JSRuntime *rt) +JS::WasIncrementalGC(JSRuntime *rt) { return rt->gcIsIncremental; } @@ -820,7 +814,7 @@ js::SetAnalysisPurgeCallback(JSRuntime *rt, AnalysisPurgeCallback callback) } JS_FRIEND_API(void) -js::NotifyDidPaint(JSRuntime *rt) +JS::NotifyDidPaint(JSRuntime *rt) { if (rt->gcZeal() == gc::ZealFrameVerifierPreValue) { gc::VerifyBarriers(rt, gc::PreBarrierVerifier); @@ -847,37 +841,37 @@ js::NotifyDidPaint(JSRuntime *rt) } JS_FRIEND_API(bool) -js::IsIncrementalGCEnabled(JSRuntime *rt) +JS::IsIncrementalGCEnabled(JSRuntime *rt) { return rt->gcIncrementalEnabled && rt->gcMode == JSGC_MODE_INCREMENTAL; } JS_FRIEND_API(bool) -js::IsIncrementalGCInProgress(JSRuntime *rt) +JS::IsIncrementalGCInProgress(JSRuntime *rt) { return (rt->gcIncrementalState != gc::NO_INCREMENTAL && !rt->gcVerifyPreData); } JS_FRIEND_API(void) -js::DisableIncrementalGC(JSRuntime *rt) +JS::DisableIncrementalGC(JSRuntime *rt) { rt->gcIncrementalEnabled = false; } JS_FRIEND_API(bool) -js::IsIncrementalBarrierNeeded(JSRuntime *rt) +JS::IsIncrementalBarrierNeeded(JSRuntime *rt) { return (rt->gcIncrementalState == gc::MARK && !rt->isHeapBusy()); } JS_FRIEND_API(bool) -js::IsIncrementalBarrierNeeded(JSContext *cx) +JS::IsIncrementalBarrierNeeded(JSContext *cx) { return IsIncrementalBarrierNeeded(cx->runtime); } JS_FRIEND_API(void) -js::IncrementalReferenceBarrier(void *ptr) +JS::IncrementalReferenceBarrier(void *ptr) { if (!ptr) return; @@ -905,13 +899,13 @@ js::IncrementalReferenceBarrier(void *ptr) } JS_FRIEND_API(void) -js::IncrementalValueBarrier(const Value &v) +JS::IncrementalValueBarrier(const Value &v) { HeapValue::writeBarrierPre(v); } JS_FRIEND_API(void) -js::PokeGC(JSRuntime *rt) +JS::PokeGC(JSRuntime *rt) { rt->gcPoke = true; } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 91848fe08b63..9a7e7eebc7bd 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -50,9 +50,6 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj extern JS_FRIEND_API(uint32_t) JS_ObjectCountDynamicSlots(JSHandleObject obj); -extern JS_FRIEND_API(void) -JS_ShrinkGCBuffers(JSRuntime *rt); - extern JS_FRIEND_API(size_t) JS_GetE4XObjectsCreated(JSContext *cx); @@ -272,13 +269,6 @@ TraceWeakMaps(WeakMapTracer *trc); extern JS_FRIEND_API(bool) AreGCGrayBitsValid(JSRuntime *rt); -/* - * Unsets the gray bit for anything reachable from |thing|. |kind| should not be - * JSTRACE_SHAPE. |thing| should be non-null. - */ -extern JS_FRIEND_API(void) -UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind); - typedef void (*GCThingCallback)(void *closure, void *gcthing); @@ -739,138 +729,12 @@ typedef Vector CompartmentVector; extern JS_FRIEND_API(const CompartmentVector&) GetRuntimeCompartments(JSRuntime *rt); -#define GCREASONS(D) \ - /* Reasons internal to the JS engine */ \ - D(API) \ - D(MAYBEGC) \ - D(LAST_CONTEXT) \ - D(DESTROY_CONTEXT) \ - D(LAST_DITCH) \ - D(TOO_MUCH_MALLOC) \ - D(ALLOC_TRIGGER) \ - D(DEBUG_GC) \ - D(DEBUG_MODE_GC) \ - D(TRANSPLANT) \ - D(RESET) \ - \ - /* Reasons from Firefox */ \ - D(DOM_WINDOW_UTILS) \ - D(COMPONENT_UTILS) \ - D(MEM_PRESSURE) \ - D(CC_WAITING) \ - D(CC_FORCED) \ - D(LOAD_END) \ - D(POST_COMPARTMENT) \ - D(PAGE_HIDE) \ - D(NSJSCONTEXT_DESTROY) \ - D(SET_NEW_DOCUMENT) \ - D(SET_DOC_SHELL) \ - D(DOM_UTILS) \ - D(DOM_IPC) \ - D(DOM_WORKER) \ - D(INTER_SLICE_GC) \ - D(REFRESH_FRAME) \ - D(FULL_GC_TIMER) \ - D(SHUTDOWN_CC) - -namespace gcreason { - -/* GCReasons will end up looking like JSGC_MAYBEGC */ -enum Reason { -#define MAKE_REASON(name) name, - GCREASONS(MAKE_REASON) -#undef MAKE_REASON - NO_REASON, - NUM_REASONS, - - /* - * For telemetry, we want to keep a fixed max bucket size over time so we - * don't have to switch histograms. 100 is conservative; as of this writing - * there are 26. But the cost of extra buckets seems to be low while the - * cost of switching histograms is high. - */ - NUM_TELEMETRY_REASONS = 100 -}; - -} /* namespace gcreason */ - -extern JS_FRIEND_API(void) -PrepareCompartmentForGC(JSCompartment *comp); - -extern JS_FRIEND_API(void) -PrepareForFullGC(JSRuntime *rt); - -extern JS_FRIEND_API(void) -PrepareForIncrementalGC(JSRuntime *rt); - -extern JS_FRIEND_API(bool) -IsGCScheduled(JSRuntime *rt); - -extern JS_FRIEND_API(void) -SkipCompartmentForGC(JSCompartment *comp); - -/* - * When triggering a GC using one of the functions below, it is first necessary - * to select the compartments to be collected. To do this, you can call - * PrepareCompartmentForGC on each compartment, or you can call PrepareForFullGC - * to select all compartments. Failing to select any compartment is an error. - */ - -extern JS_FRIEND_API(void) -GCForReason(JSRuntime *rt, gcreason::Reason reason); - -extern JS_FRIEND_API(void) -ShrinkingGC(JSRuntime *rt, gcreason::Reason reason); - -extern JS_FRIEND_API(void) -IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0); - -extern JS_FRIEND_API(void) -FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason); - -enum GCProgress { - /* - * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END - * callbacks. During an incremental GC, the sequence of callbacks is as - * follows: - * JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice) - * JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice) - * ... - * JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice) - */ - - GC_CYCLE_BEGIN, - GC_SLICE_BEGIN, - GC_SLICE_END, - GC_CYCLE_END -}; - -struct JS_FRIEND_API(GCDescription) { - bool isCompartment; - - GCDescription(bool isCompartment) - : isCompartment(isCompartment) {} - - jschar *formatMessage(JSRuntime *rt) const; - jschar *formatJSON(JSRuntime *rt, uint64_t timestamp) const; -}; - -typedef void -(* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc); - -extern JS_FRIEND_API(GCSliceCallback) -SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback); - typedef void (* AnalysisPurgeCallback)(JSRuntime *rt, JSFlatString *desc); extern JS_FRIEND_API(AnalysisPurgeCallback) SetAnalysisPurgeCallback(JSRuntime *rt, AnalysisPurgeCallback callback); -/* Was the most recent GC run incrementally? */ -extern JS_FRIEND_API(bool) -WasIncrementalGC(JSRuntime *rt); - typedef JSBool (* DOMInstanceClassMatchesProto)(JSHandleObject protoObject, uint32_t protoID, uint32_t depth); @@ -885,74 +749,6 @@ SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks); extern JS_FRIEND_API(const DOMCallbacks *) GetDOMCallbacks(JSRuntime *rt); -/* - * Signals a good place to do an incremental slice, because the browser is - * drawing a frame. - */ -extern JS_FRIEND_API(void) -NotifyDidPaint(JSRuntime *rt); - -extern JS_FRIEND_API(bool) -IsIncrementalGCEnabled(JSRuntime *rt); - -JS_FRIEND_API(bool) -IsIncrementalGCInProgress(JSRuntime *rt); - -extern JS_FRIEND_API(void) -DisableIncrementalGC(JSRuntime *rt); - -extern JS_FRIEND_API(bool) -IsIncrementalBarrierNeeded(JSRuntime *rt); - -extern JS_FRIEND_API(bool) -IsIncrementalBarrierNeeded(JSContext *cx); - -extern JS_FRIEND_API(void) -IncrementalReferenceBarrier(void *ptr); - -extern JS_FRIEND_API(void) -IncrementalValueBarrier(const Value &v); - -extern JS_FRIEND_API(void) -PokeGC(JSRuntime *rt); - -class ObjectPtr -{ - JSObject *value; - - public: - ObjectPtr() : value(NULL) {} - - ObjectPtr(JSObject *obj) : value(obj) {} - - /* Always call finalize before the destructor. */ - ~ObjectPtr() { JS_ASSERT(!value); } - - void finalize(JSRuntime *rt) { - if (IsIncrementalBarrierNeeded(rt)) - IncrementalReferenceBarrier(value); - value = NULL; - } - - void init(JSObject *obj) { value = obj; } - - JSObject *get() const { return value; } - - void writeBarrierPre(JSRuntime *rt) { - IncrementalReferenceBarrier(value); - } - - ObjectPtr &operator=(JSObject *obj) { - IncrementalReferenceBarrier(value); - value = obj; - return *this; - } - - JSObject &operator*() const { return *value; } - JSObject *operator->() const { return value; } - operator JSObject *() const { return value; } -}; - extern JS_FRIEND_API(JSObject *) GetTestingFunctions(JSContext *cx); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 412b10ffc1be..c8003a0c1a9e 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4513,8 +4513,8 @@ js::PrepareForDebugGC(JSRuntime *rt) PrepareForFullGC(rt); } -void -js::ShrinkGCBuffers(JSRuntime *rt) +JS_FRIEND_API(void) +JS::ShrinkGCBuffers(JSRuntime *rt) { AutoLockGC lock(rt); JS_ASSERT(!rt->isHeapBusy()); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 26ac5108e761..b9e99e22ee76 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -544,9 +544,6 @@ TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason); extern void MaybeGC(JSContext *cx); -extern void -ShrinkGCBuffers(JSRuntime *rt); - extern void ReleaseAllJITCode(FreeOp *op); diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 2481b816356b..f348849e74eb 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -513,7 +513,7 @@ private: if (delegateMightNeedMarking && kkind == JSTRACE_OBJECT) { JSObject *kdelegate = js::GetWeakmapKeyDelegate((JSObject *)k); if (kdelegate && !xpc_IsGrayGCThing(kdelegate)) { - js::UnmarkGrayGCThingRecursively(k, JSTRACE_OBJECT); + JS::UnmarkGrayGCThingRecursively(k, JSTRACE_OBJECT); tracer->mAnyMarked = true; } } @@ -523,7 +523,7 @@ private: (!m || !xpc_IsGrayGCThing(m)) && vkind != JSTRACE_SHAPE) { - js::UnmarkGrayGCThingRecursively(v, vkind); + JS::UnmarkGrayGCThingRecursively(v, vkind); tracer->mAnyMarked = true; } diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 71c553174f7a..7ffef57f9ae5 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -15,6 +15,7 @@ #include "jspubtd.h" #include "jsproxy.h" #include "js/HeapAPI.h" +#include "js/GCAPI.h" #include "nsISupports.h" #include "nsIPrincipal.h" diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index f3265620b184..0067dbe46aee 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -84,7 +84,7 @@ }, "GC_REASON_2": { "kind": "enumerated", - "n_values": "js::gcreason::NUM_TELEMETRY_REASONS", + "n_values": "JS::gcreason::NUM_TELEMETRY_REASONS", "description": "Reason (enum value) for initiating a GC" }, "GC_IS_COMPARTMENTAL": { diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 6594be086d45..4f970f4f0042 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -27,6 +27,7 @@ #include "mozilla/Services.h" #include "jsapi.h" #include "jsfriendapi.h" +#include "js/GCAPI.h" #include "nsStringGlue.h" #include "nsITelemetry.h" #include "nsIFile.h" diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index f43dbd1c1dbd..49bdbfa80c80 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -2820,7 +2820,7 @@ nsCycleCollector::FixGrayBits(bool aForceGC) // mJSRuntime->Collect() must be called from the main thread, // because it invokes XPCJSRuntime::GCCallback(cx, JSGC_BEGIN) // which returns false if not in the main thread. - mJSRuntime->Collect(aForceGC ? js::gcreason::SHUTDOWN_CC : js::gcreason::CC_FORCED); + mJSRuntime->Collect(aForceGC ? JS::gcreason::SHUTDOWN_CC : JS::gcreason::CC_FORCED); timeLog.Checkpoint("GC()"); }