diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index bcc4d6ad6c..7c1ba83cd4 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -108,6 +108,8 @@ const size_t gStackSize = 8192; // Maximum amount of time that should elapse between incremental GC slices #define NS_INTERSLICE_GC_DELAY 100 // ms +#define NS_INTERSLICE_GC_BUDGET 5 // ms + // Maximum amount of time that should elapse between incremental GC slices #define NS_INTERSLICE_GC_DELAY 100 // ms @@ -189,6 +191,8 @@ static bool sNeedsFullGC = false; static bool sNeedsGCAfterCC = false; static bool sIncrementalCC = false; static int32_t sActiveIntersliceGCBudget = 5; // ms; +static bool sDidPaintAfterPreviousICCSlice = false; +static uint32_t sCCTimerFireCount = 0; static nsScriptNameSpaceManager *gNameSpaceManager; static PRTime sFirstCollectionTime; @@ -1531,7 +1535,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, //static void -nsJSContext::RunCycleCollectorSlice() +nsJSContext::RunCycleCollectorSlice(const TimeStamp& aDeadline) { if (!NS_IsMainThread()) { return; @@ -1637,7 +1641,7 @@ ICCTimerFired(nsITimer* aTimer, void* aClosure) } } - nsJSContext::RunCycleCollectorSlice(); + nsJSContext::RunCycleCollectorSlice(TimeStamp()); } //static @@ -1816,6 +1820,7 @@ InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure) // We use longer budgets when the CC has been locked out but the CC has tried // to run since that means we may have significant amount garbage to collect // and better to GC in several longer slices than in a very long one. + TimeStamp aDeadline; // no idle deadline available in timer callback int64_t budget = aDeadline.IsNull() ? int64_t(sActiveIntersliceGCBudget * 2) : int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds()); @@ -1829,8 +1834,8 @@ InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure) std::max((double)budget, percentOfLockedTime * maxSliceGCBudget)); } - uintptr_t reason = reinterpret_cast(aData); - nsJSContext::GarbageCollectNow(aData ? + uintptr_t reason = reinterpret_cast(aClosure); + nsJSContext::GarbageCollectNow(aClosure ? static_cast(reason) : JS::gcreason::INTER_SLICE_GC, nsJSContext::IncrementalGC, @@ -1906,9 +1911,10 @@ CCTimerFired(nsITimer *aTimer, void *aClosure) int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1); bool isLateTimerFire = sCCRunnerFireCount > numEarlyTimerFires; uint32_t suspected = nsCycleCollector_suspectedCount(); + TimeStamp deadline; // no idle deadline in timer callback if (isLateTimerFire && ShouldTriggerCC(suspected)) { if (sCCRunnerFireCount == numEarlyTimerFires + 1) { - FireForgetSkippable(suspected, true, aDeadline); + FireForgetSkippable(suspected, true, deadline); didDoWork = true; if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) { // Our efforts to avoid a CC have failed, so we return to let the @@ -1919,14 +1925,14 @@ CCTimerFired(nsITimer *aTimer, void *aClosure) // We are in the final timer fire and still meet the conditions for // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if // any because that will allow us to include the GC time in the CC pause. - nsJSContext::RunCycleCollectorSlice(aDeadline); + nsJSContext::RunCycleCollectorSlice(deadline); didDoWork = true; } } else if (((sPreviousSuspectedCount + 100) <= suspected) || (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) { // Only do a forget skippable if there are more than a few new objects // or we're doing the initial forget skippables. - FireForgetSkippable(suspected, false, aDeadline); + FireForgetSkippable(suspected, false, deadline); didDoWork = true; } @@ -2034,7 +2040,7 @@ nsJSContext::RunNextCollectorTimer() // static void -nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay) +nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay, JSObject* aObj) { sNeedsFullGC = sNeedsFullGC || aReason != JS::gcreason::CC_WAITING; @@ -2147,6 +2153,16 @@ nsJSContext::KillGCTimer() } } +//static +void +nsJSContext::KillCCTimer() +{ + if (sCCTimer) { + sCCTimer->Cancel(); + NS_RELEASE(sCCTimer); + } +} + void nsJSContext::KillFullGCTimer() { diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 20ed35b53e..3451bd5373 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -91,7 +91,7 @@ public: int32_t aExtraForgetSkippableCalls = 0); // Run a cycle collector slice, using a heuristic to decide how long to run it. - static void RunCycleCollectorSlice(); + static void RunCycleCollectorSlice(const mozilla::TimeStamp& aDeadline = mozilla::TimeStamp()); // Run a cycle collector slice, using the given work budget. static void RunCycleCollectorWorkSlice(int64_t aWorkBudget); @@ -105,9 +105,11 @@ public: static void RunNextCollectorTimer(); - static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0); + static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0, JSObject* aObj = nullptr); static void KillGCTimer(); + static void KillCCTimer(); + static void PokeShrinkingGC(); static void KillShrinkingGCTimer(); @@ -117,6 +119,8 @@ public: static void KillFullGCTimer(); static void KillInterSliceGCRunner(); + static void NotifyDidPaint(); + // Calling LikelyShortLivingObjectCreated() makes a GC more likely. static void LikelyShortLivingObjectCreated(); diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 46fec7d4c3..32335e5921 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -323,7 +323,7 @@ typedef void * can be called off the main thread. */ struct JSStringFinalizer { - void (*finalize)(const JSStringFinalizer* fin, char16_t* chars); + void (*finalize)(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars); }; @@ -346,6 +346,7 @@ struct Zone; D(OUT_OF_NURSERY) \ D(EVICT_NURSERY) \ D(FULL_STORE_BUFFER) \ + D(FULL_SLOT_BUFFER) \ D(SHARED_MEMORY_LIMIT) \ D(UNUSED1) \ D(INCREMENTAL_TOO_SLOW) \ @@ -986,6 +987,9 @@ JS_UpdateWeakPointerAfterGCUnbarriered(JSObject** objp); extern JS_PUBLIC_API(void) JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value); +extern JS_PUBLIC_API(void) +JS_SetGGCMode(JSContext* cx, bool enabled); + extern JS_PUBLIC_API(void) JS_ResetGCParameter(JSContext* cx, JSGCParamKey key); diff --git a/js/src/ds/Bitmap.h b/js/src/ds/Bitmap.h new file mode 100644 index 0000000000..6f993ccd23 --- /dev/null +++ b/js/src/ds/Bitmap.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 ds_Bitmap_h +#define ds_Bitmap_h + +#include "mozilla/PodOperations.h" + +#include +#include + +namespace js { + +// DenseBitmap is an simple dynamically-sized bitmap backed by a vector of +// words. +class DenseBitmap +{ + using Word = uintptr_t; + + Word* data_; + size_t numWords_; + + public: + DenseBitmap() + : data_(nullptr), numWords_(0) + {} + + ~DenseBitmap() + { + js_free(data_); + } + + bool ensureSpace(size_t newWords) + { + if (newWords <= numWords_) + return true; + + size_t newSize = sizeof(Word) * newWords; + Word* newData = static_cast(js_realloc(data_, newSize)); + if (!newData) + return false; + + mozilla::PodZero(newData + numWords_, newWords - numWords_); + data_ = newData; + numWords_ = newWords; + return true; + } + + void copyBitsFrom(size_t dstStart, size_t wordCount, const Word* src) + { + MOZ_ASSERT(dstStart + wordCount <= numWords_); + memcpy(data_ + dstStart, src, sizeof(Word) * wordCount); + } + + void bitwiseAndWith(const DenseBitmap& other) + { + MOZ_ASSERT(numWords_ == other.numWords_); + for (size_t i = 0; i < numWords_; i++) + data_[i] &= other.data_[i]; + } + + void bitwiseOrRangeInto(size_t srcStart, size_t wordCount, Word* dst) const + { + MOZ_ASSERT(srcStart + wordCount <= numWords_); + for (size_t i = 0; i < wordCount; i++) + dst[i] |= data_[srcStart + i]; + } + + void bitwiseOrInto(DenseBitmap& other) const + { + MOZ_ASSERT(numWords_ == other.numWords_); + for (size_t i = 0; i < numWords_; i++) + other.data_[i] |= data_[i]; + } + + void bitwiseOrWith(const DenseBitmap& other) + { + MOZ_ASSERT(numWords_ == other.numWords_); + for (size_t i = 0; i < numWords_; i++) + data_[i] |= other.data_[i]; + } + + bool getBit(size_t bit) const + { + size_t wordIndex = bit / (sizeof(Word) * 8); + size_t bitIndex = bit % (sizeof(Word) * 8); + MOZ_ASSERT(wordIndex < numWords_); + return data_[wordIndex] & (Word(1) << bitIndex); + } + + void setBit(size_t bit) + { + size_t wordIndex = bit / (sizeof(Word) * 8); + size_t bitIndex = bit % (sizeof(Word) * 8); + MOZ_ASSERT(wordIndex < numWords_); + data_[wordIndex] |= (Word(1) << bitIndex); + } + + MOZ_MUST_USE bool init() + { + return true; + } + + Word* data() { return data_; } + size_t numWords() const { return numWords_; } +}; + +} // namespace js + +#endif // ds_Bitmap_h diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index 70729f7016..5da2abf865 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -613,7 +613,7 @@ struct ChunkBitmap uintptr_t** wordp, uintptr_t* maskp) { MOZ_ASSERT(size_t(colorBit) < MarkBitsPerCell); - detail::GetGCThingMarkWordAndMask(uintptr_t(cell), colorBit, wordp, maskp); + detail::GetGCThingMarkWordAndMask(uintptr_t(cell), uint32_t(colorBit), wordp, maskp); } MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool markBit(const TenuredCell* cell, ColorBit colorBit) { @@ -637,7 +637,7 @@ struct ChunkBitmap // The return value indicates if the cell went from unmarked to marked. MOZ_ALWAYS_INLINE bool markIfUnmarked(const TenuredCell* cell, MarkColor color) { uintptr_t* word, mask; - getMarkWordAndMask(cell, BLACK, &word, &mask); + getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask); if (*word & mask) return false; if (color == MarkColor::Black) { @@ -647,7 +647,7 @@ struct ChunkBitmap * We use getMarkWordAndMask to recalculate both mask and word as * doing just mask << color may overflow the mask. */ - getMarkWordAndMask(cell, color, &word, &mask); + getMarkWordAndMask(cell, ColorBit(uint32_t(color)), &word, &mask); if (*word & mask) return false; *word |= mask; @@ -657,8 +657,8 @@ struct ChunkBitmap MOZ_ALWAYS_INLINE void markBlack(const TenuredCell* cell) { uintptr_t* word, mask; - getMarkWordAndMask(cell, color, &word, &mask); - *word &= ~mask; + getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask); + *word |= mask; } MOZ_ALWAYS_INLINE void copyMarkBit(TenuredCell* dst, const TenuredCell* src, diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 7c08aa2721..0c53f7e66c 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -9,6 +9,7 @@ #define gc_Nursery_h #include "mozilla/EnumeratedArray.h" +#include "mozilla/TimeStamp.h" #include "js/Class.h" #include "js/HeapAPI.h" @@ -381,6 +382,8 @@ class Nursery */ unsigned chunkCountLimit_; + mozilla::Atomic minorGCTriggerReason_; + mozilla::TimeDuration timeInChunkAlloc_; /* Promotion rate for the previous minor collection. */ @@ -471,7 +474,7 @@ class Nursery * sweep. This is because this structure is used to help implement * stable object hashing and we have to break the cycle somehow. */ - using CellsWithUniqueIdSet = HashSet, SystemAllocPolicy>; + using CellsWithUniqueIdSet = HashSet, SystemAllocPolicy>; CellsWithUniqueIdSet cellsWithUid_; struct SweepAction; @@ -502,7 +505,6 @@ class Nursery MOZ_ALWAYS_INLINE uintptr_t currentEnd() const; uintptr_t position() const { return position_; } - void* addressOfPosition() const { return (void*)&position_; } JSRuntime* runtime() const { return runtime_; } diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index d00c062dc8..2b6175e24b 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -625,6 +625,13 @@ struct Zone : public JS::shadow::Zone, js::ZoneGroupData gcPreserveCode_; js::ZoneGroupData keepShapeTables_; + js::ZoneGroupData markedAtoms_; + + public: + js::DenseBitmap& markedAtoms() { return markedAtoms_.ref(); } + + private: + // Allow zones to be linked into a list friend class js::gc::ZoneList; static Zone * const NotOnList; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 652d730ab0..136a72e411 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -837,10 +837,10 @@ JS_IsBuiltinFunctionConstructor(JSFunction* fun); * See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference */ -extern JS_PUBLIC_API(JSContext*) -JS_NewContext(uint32_t maxbytes, - uint32_t maxNurseryBytes = JS::DefaultNurseryBytes, - JSRuntime* parentRuntime = nullptr); +extern JS_PUBLIC_API(JSContext*) +JS_NewContext(uint32_t maxbytes, + uint32_t maxNurseryBytes = JS::DefaultNurseryBytes, + JSContext* parentContext = nullptr); // The methods below for controlling the active context in a cooperatively // multithreaded runtime are not threadsafe, and the caller must ensure they diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 3a0035a760..7d7d9fab96 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -1030,7 +1030,7 @@ class NativeObject : public ShapedObject for (size_t i = 0; i < count; i++) { const Value& v = elements_[start + i]; if (v.isObject() && IsInsideNursery(&v.toObject())) { - JS::shadow::Runtime* shadowRuntime = shadowRuntimeFromMainThread(); + JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread()); shadowRuntime->gcStoreBufferPtr()->putSlot(this, HeapSlot::Element, start + i, count - i); return; diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index 65590f5dfd..6cd9c37773 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -160,7 +160,7 @@ class TraceLoggerThread private: typedef HashMap, + PointerHasher, SystemAllocPolicy> PointerHashMap; typedef HashMap struct IsParameterStorageClass> : public mozilla::TrueType {}; +template +struct StorensAutoPtrPassByRRef +{ + typedef nsAutoPtr stored_type; + typedef nsAutoPtr passed_type; + stored_type m; + template + MOZ_IMPLICIT StorensAutoPtrPassByRRef(A&& a) : m(mozilla::Forward(a)) {} + passed_type PassAsParameter() { return mozilla::Move(m); } +}; +template +struct IsParameterStorageClass> + : public mozilla::TrueType {}; + template struct StorePtrPassByPtr { @@ -1022,6 +1036,12 @@ struct SmartPointerStorageClass StoreCopyPassByConstLRef> {}; +template +struct SmartPointerStorageClass> +{ + typedef StorensAutoPtrPassByRRef Type; +}; + template struct NonLValueReferenceStorageClass : mozilla::Conditional::value,