diff --git a/memory/build/PHC.cpp b/memory/build/PHC.cpp index a17314a0b394..f6e02b2c22d2 100644 --- a/memory/build/PHC.cpp +++ b/memory/build/PHC.cpp @@ -337,32 +337,13 @@ static const size_t kAllPagesJemallocSize = kAllPagesSize - kPageSize; // The maximum time. static const Time kMaxTime = ~(Time(0)); -// The average delay before doing any page allocations at the start of a -// process. Note that roughly 1 million allocations occur in the main process -// while starting the browser. The delay range is 1..kAvgFirstAllocDelay*2. -static const Delay kAvgFirstAllocDelay = 64 * 1024; - -// The average delay until the next attempted page allocation, once we get past -// the first delay. The delay range is 1..kAvgAllocDelay*2. -static const Delay kAvgAllocDelay = 16 * 1024; - -// The average delay before reusing a freed page. Should be significantly larger -// than kAvgAllocDelay, otherwise there's not much point in having it. The delay -// range is (kAvgAllocDelay / 2)..(kAvgAllocDelay / 2 * 3). This is different to -// the other delay ranges in not having a minimum of 1, because that's such a -// short delay that there is a high likelihood of bad stacks in any crash -// report. -static const Delay kAvgPageReuseDelay = 256 * 1024; - -// Truncate aRnd to the range (1 .. AvgDelay*2). If aRnd is random, this +// Truncate aRnd to the range (1 .. aAvgDelay*2). If aRnd is random, this // results in an average value of aAvgDelay + 0.5, which is close enough to -// aAvgDelay. aAvgDelay must be a power-of-two (otherwise it will crash) for -// speed. -template -constexpr Delay Rnd64ToDelay(uint64_t aRnd) { - static_assert(IsPowerOfTwo(AvgDelay), "must be a power of two"); +// aAvgDelay. aAvgDelay must be a power-of-two for speed. +constexpr Delay Rnd64ToDelay(Delay aAvgDelay, uint64_t aRnd) { + MOZ_ASSERT(IsPowerOfTwo(aAvgDelay), "must be a power of two"); - return aRnd % (AvgDelay * 2) + 1; + return (aRnd & (aAvgDelay * 2 - 1)) + 1; } // Maps a pointer to a PHC-specific structure: @@ -437,10 +418,11 @@ class GAtomic { static void SetAllocDelay(Delay aAllocDelay) { sAllocDelay = aAllocDelay; } - static bool AllocDelayHasWrapped() { + static bool AllocDelayHasWrapped(Delay aAvgAllocDelay, + Delay aAvgFirstAllocDelay) { // Delay is unsigned so we can't test for less that zero. Instead test if // it has wrapped around by comparing with the maximum value we ever use. - return sAllocDelay > 2 * std::max(kAvgAllocDelay, kAvgFirstAllocDelay); + return sAllocDelay > 2 * std::max(aAvgAllocDelay, aAvgFirstAllocDelay); } private: @@ -875,7 +857,7 @@ class GMut { void SetState(PHCState aState) { if (mPhcState != PHCState::Enabled && aState == PHCState::Enabled) { MutexAutoLock lock(GMut::sMutex); - GAtomic::Init(Rnd64ToDelay(Random64(lock))); + GAtomic::Init(Rnd64ToDelay(mAvgFirstAllocDelay, Random64(lock))); } mPhcState = aState; @@ -945,6 +927,32 @@ class GMut { // eventually get the update. Atomic mPhcState = Atomic(DEFAULT_STATE); + + // The average delay before doing any page allocations at the start of a + // process. Note that roughly 1 million allocations occur in the main process + // while starting the browser. The delay range is 1..gAvgFirstAllocDelay*2. + Delay mAvgFirstAllocDelay = 64 * 1024; + + // The average delay until the next attempted page allocation, once we get + // past the first delay. The delay range is 1..kAvgAllocDelay*2. + Delay mAvgAllocDelay = 16 * 1024; + + // The average delay before reusing a freed page. Should be significantly + // larger than kAvgAllocDelay, otherwise there's not much point in having it. + // The delay range is (kAvgAllocDelay / 2)..(kAvgAllocDelay / 2 * 3). This is + // different to the other delay ranges in not having a minimum of 1, because + // that's such a short delay that there is a high likelihood of bad stacks in + // any crash report. + Delay mAvgPageReuseDelay = 256 * 1024; + + public: + Delay GetAvgAllocDelay(const MutexAutoLock&) { return mAvgAllocDelay; } + Delay GetAvgFirstAllocDelay(const MutexAutoLock&) { + return mAvgFirstAllocDelay; + } + Delay GetAvgPageReuseDelay(const MutexAutoLock&) { + return mAvgPageReuseDelay; + } }; Mutex GMut::sMutex; @@ -1040,13 +1048,11 @@ class GTls { static void EnableOnCurrentThread() { MOZ_ASSERT(GTls::tlsIsDisabled.get()); - uint64_t rand; - if (GAtomic::AllocDelayHasWrapped()) { - { - MutexAutoLock lock(GMut::sMutex); - rand = gMut->Random64(lock); - } - GAtomic::SetAllocDelay(Rnd64ToDelay(rand)); + MutexAutoLock lock(GMut::sMutex); + Delay avg_delay = gMut->GetAvgAllocDelay(lock); + Delay avg_first_delay = gMut->GetAvgFirstAllocDelay(lock); + if (GAtomic::AllocDelayHasWrapped(avg_delay, avg_first_delay)) { + GAtomic::SetAllocDelay(Rnd64ToDelay(avg_delay, gMut->Random64(lock))); } tlsIsDisabled.set(false); } @@ -1185,7 +1191,8 @@ static void* MaybePageAlloc(const Maybe& aArenaId, size_t aReqSize, MutexAutoLock lock(GMut::sMutex); Time now = GAtomic::Now(); - Delay newAllocDelay = Rnd64ToDelay(gMut->Random64(lock)); + Delay newAllocDelay = + Rnd64ToDelay(gMut->GetAvgAllocDelay(lock), gMut->Random64(lock)); // We start at a random page alloc and wrap around, to ensure pages get even // amounts of use. @@ -1312,8 +1319,9 @@ inline void* MozJemallocPHC::malloc(size_t aReqSize) { } static Delay ReuseDelay(GMutLock aLock) { - return (kAvgPageReuseDelay / 2) + - Rnd64ToDelay(gMut->Random64(aLock)); + Delay avg_reuse_delay = gMut->GetAvgPageReuseDelay(aLock); + return (avg_reuse_delay / 2) + + Rnd64ToDelay(avg_reuse_delay / 2, gMut->Random64(aLock)); } // This handles both calloc and moz_arena_calloc.