| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "CommonMemoryPch.h"
- IdleDecommitPageAllocator::IdleDecommitPageAllocator(AllocationPolicyManager * policyManager, PageAllocatorType type,
- Js::ConfigFlagsTable& flagTable,
- uint maxFreePageCount, uint maxIdleFreePageCount,
- bool zeroPages,
- #if ENABLE_BACKGROUND_PAGE_FREEING
- BackgroundPageQueue * backgroundPageQueue,
- #endif
- uint maxAllocPageCount, bool enableWriteBarrier) :
- #ifdef IDLE_DECOMMIT_ENABLED
- idleDecommitTryEnterWaitFactor(0),
- hasDecommitTimer(false),
- hadDecommitTimer(false),
- #endif
- PageAllocator(policyManager, flagTable, type, maxFreePageCount, zeroPages,
- #if ENABLE_BACKGROUND_PAGE_FREEING
- backgroundPageQueue,
- #endif
- maxAllocPageCount, 0, false, false, GetCurrentProcess(), enableWriteBarrier),
- maxIdleDecommitFreePageCount(maxIdleFreePageCount),
- maxNonIdleDecommitFreePageCount(maxFreePageCount)
- {
- // if maxIdle is the same as max free, disable idleDecommit but setting the entry count to 1
- this->idleDecommitEnterCount = (maxIdleFreePageCount == maxFreePageCount);
- #ifdef IDLE_DECOMMIT_ENABLED
- #if DBG_DUMP
- idleDecommitCount = 0;
- #endif
- #endif
- }
- void
- IdleDecommitPageAllocator::EnterIdleDecommit()
- {
- this->idleDecommitEnterCount++;
- if (this->idleDecommitEnterCount != 1)
- {
- return;
- }
- #ifdef IDLE_DECOMMIT_ENABLED
- if (!cs.TryEnter())
- {
- AutoResetWaitingToEnterIdleDecommitFlag autoResetWaitingToEnterIdleDecommitFlag(this);
- #ifdef ENABLE_BASIC_TELEMETRY
- AllocatorDecommitStats * decommitStats = this->GetDecommitStats();
- decommitStats->lastEnterLeaveIdleDecommitTick = Js::Tick::Now();
- #endif
- cs.Enter();
- #ifdef ENABLE_BASIC_TELEMETRY
- decommitStats->lastEnterLeaveIdleDecommitCSWaitTime = Js::Tick::Now() - decommitStats->lastEnterLeaveIdleDecommitTick;
- decommitStats->totalEnterLeaveIdleDecommitCSWaitTime += decommitStats->lastEnterLeaveIdleDecommitCSWaitTime;
- if (decommitStats->lastEnterLeaveIdleDecommitCSWaitTime > decommitStats->maxEnterLeaveIdleDecommitCSWaitTime)
- {
- decommitStats->maxEnterLeaveIdleDecommitCSWaitTime = decommitStats->lastEnterLeaveIdleDecommitCSWaitTime;
- }
- #endif
- }
- this->isUsed = false;
- this->hadDecommitTimer = hasDecommitTimer;
- PAGE_ALLOC_VERBOSE_TRACE(_u("EnterIdleDecommit"));
- if (hasDecommitTimer)
- {
- // Cancel the decommit timer
- Assert(this->maxFreePageCount == maxIdleDecommitFreePageCount);
- hasDecommitTimer = false;
- PAGE_ALLOC_TRACE(_u("Cancel Decommit Timer"));
- }
- else
- {
- // Switch to maxIdleDecommitFreePageCount
- Assert(this->maxFreePageCount == maxNonIdleDecommitFreePageCount);
- Assert(minFreePageCount == 0);
- this->maxFreePageCount = maxIdleDecommitFreePageCount;
- }
- cs.Leave();
- Assert(!hasDecommitTimer);
- #else
- Assert(this->maxFreePageCount == maxNonIdleDecommitFreePageCount);
- this->maxFreePageCount = maxIdleDecommitFreePageCount;
- #endif
- }
- IdleDecommitSignal
- IdleDecommitPageAllocator::LeaveIdleDecommit(bool allowTimer)
- {
- Assert(this->idleDecommitEnterCount > 0);
- Assert(this->maxFreePageCount == maxIdleDecommitFreePageCount);
- #ifdef ENABLE_BASIC_TELEMETRY
- AllocatorDecommitStats * decommitStats = this->GetDecommitStats();
- decommitStats->lastLeaveDecommitRegion = Js::Tick::Now();
- #endif
- #ifdef IDLE_DECOMMIT_ENABLED
- Assert(!hasDecommitTimer);
- #endif
- this->idleDecommitEnterCount--;
- if (this->idleDecommitEnterCount != 0)
- {
- return IdleDecommitSignal_None;
- }
- #ifdef IDLE_DECOMMIT_ENABLED
- if (allowTimer)
- {
- if (!cs.TryEnter())
- {
- AutoResetWaitingToEnterIdleDecommitFlag autoResetWaitingToEnterIdleDecommitFlag(this);
- #ifdef ENABLE_BASIC_TELEMETRY
- decommitStats->lastEnterLeaveIdleDecommitTick = Js::Tick::Now();
- #endif
- cs.Enter();
- #ifdef ENABLE_BASIC_TELEMETRY
- decommitStats->lastEnterLeaveIdleDecommitCSWaitTime = Js::Tick::Now() - decommitStats->lastEnterLeaveIdleDecommitTick;
- decommitStats->totalEnterLeaveIdleDecommitCSWaitTime += decommitStats->lastEnterLeaveIdleDecommitCSWaitTime;
- if (decommitStats->lastEnterLeaveIdleDecommitCSWaitTime > decommitStats->maxEnterLeaveIdleDecommitCSWaitTime)
- {
- decommitStats->maxEnterLeaveIdleDecommitCSWaitTime = decommitStats->lastEnterLeaveIdleDecommitCSWaitTime;
- }
- #endif
- }
- PAGE_ALLOC_VERBOSE_TRACE(_u("LeaveIdleDecommit"));
- Assert(maxIdleDecommitFreePageCount != maxNonIdleDecommitFreePageCount);
- IdleDecommitSignal idleDecommitSignal = IdleDecommitSignal_None;
- if (freePageCount == 0 && !isUsed && !hadDecommitTimer)
- {
- Assert(minFreePageCount == 0);
- Assert(minFreePageCount == debugMinFreePageCount);
- // Nothing to decommit, it isn't used, and there was no timer before.
- // Just switch it back to non idle decommit mode
- this->maxFreePageCount = maxNonIdleDecommitFreePageCount;
- }
- else
- {
- UpdateMinFreePageCount();
- hasDecommitTimer = true;
- idleDecommitSignal = IdleDecommitSignal_NeedTimer;
- if (isUsed)
- {
- // Reschedule the timer
- decommitTime = ::GetTickCount() + IdleDecommitTimeout;
- PAGE_ALLOC_TRACE( _u("Schedule idle decommit at %d (%d)"), decommitTime, IdleDecommitTimeout);
- }
- else
- {
- int timeDiff = (int)decommitTime - ::GetTickCount();
- if (timeDiff < 20)
- {
- idleDecommitSignal = IdleDecommitSignal_NeedSignal;
- }
- PAGE_ALLOC_TRACE(_u("Reschedule idle decommit at %d (%d)"), decommitTime, decommitTime - ::GetTickCount());
- }
- }
- cs.Leave();
- return idleDecommitSignal;
- }
- #endif
- this->maxFreePageCount = maxNonIdleDecommitFreePageCount;
- __super::DecommitNow();
- ClearMinFreePageCount();
- return IdleDecommitSignal_None;
- }
- #ifdef IDLE_DECOMMIT_ENABLED
- void
- IdleDecommitPageAllocator::DecommitNow(bool all)
- {
- SuspendIdleDecommit();
- // If we are in non-idle-decommit mode, then always decommit all.
- // Otherwise, we will end up with some un-decommitted pages and get confused later.
- if (maxFreePageCount == maxNonIdleDecommitFreePageCount)
- all = true;
- __super::DecommitNow(all);
- if (all)
- {
- if (this->hasDecommitTimer)
- {
- Assert(idleDecommitEnterCount == 0);
- Assert(this->maxFreePageCount == maxIdleDecommitFreePageCount);
- this->hasDecommitTimer = false;
- this->maxFreePageCount = maxNonIdleDecommitFreePageCount;
- }
- else
- {
- Assert((idleDecommitEnterCount > 0? maxIdleDecommitFreePageCount : maxNonIdleDecommitFreePageCount)
- == this->maxFreePageCount);
- }
- ClearMinFreePageCount();
- }
- else
- {
- ResetMinFreePageCount();
- }
- ResumeIdleDecommit();
- }
- DWORD
- IdleDecommitPageAllocator::IdleDecommit()
- {
- // We can check hasDecommitTimer outside of the lock because when it change to true
- // the Recycler::concurrentIdleDecommitEvent will signal and we try to IdleDecommit again
- // If it change to false, we check again when we acquired the lock
- if (!hasDecommitTimer)
- {
- return INFINITE;
- }
- if (!cs.TryEnter())
- {
- // Failed to acquire the lock, wait for a variable time.
- PAGE_ALLOC_TRACE(_u("IdleDecommit Retry"));
- // Varies the wait time between 11 - 99
- idleDecommitTryEnterWaitFactor++;
- if (idleDecommitTryEnterWaitFactor >= 10)
- {
- idleDecommitTryEnterWaitFactor = 1;
- }
- DWORD waitTime = 11 * idleDecommitTryEnterWaitFactor;
- return waitTime; // Retry time
- }
- idleDecommitTryEnterWaitFactor = 0;
- DWORD waitTime = INFINITE;
- if (hasDecommitTimer)
- {
- Assert(this->maxFreePageCount == maxIdleDecommitFreePageCount);
- int timediff = (int)(decommitTime - ::GetTickCount());
- if (timediff >= 20) // Ignore time diff is it is < 20 since the system timer doesn't have that high of precision anyways
- {
- waitTime = (DWORD)timediff;
- }
- else
- {
- // Do the decommit in normal priority so that we don't block the main thread for too long
- PAGE_ALLOC_TRACE(_u("IdleDecommit"));
- #if DBG_DUMP
- idleDecommitCount++;
- #endif
- __super::DecommitNow();
- hasDecommitTimer = false;
- ClearMinFreePageCount();
- this->maxFreePageCount = maxNonIdleDecommitFreePageCount;
- }
- }
- cs.Leave();
- return waitTime;
- }
- #endif
- void
- IdleDecommitPageAllocator::Prime(uint primePageCount)
- {
- while (this->freePageCount < primePageCount)
- {
- PageSegment * segment = AddPageSegment(emptySegments);
- if (segment == nullptr)
- {
- return;
- }
- segment->Prime();
- }
- }
- #if DBG
- bool
- IdleDecommitPageAllocator::HasMultiThreadAccess() const
- {
- #ifdef IDLE_DECOMMIT_ENABLED
- return this->hasDecommitTimer && !cs.IsLocked();
- #else
- return false;
- #endif
- }
- void
- IdleDecommitPageAllocator::ShutdownIdleDecommit()
- {
- // The recycler thread should have died already
- // Just set the state
- idleDecommitEnterCount = 1;
- #ifdef IDLE_DECOMMIT_ENABLED
- hasDecommitTimer = false;
- #endif
- }
- #endif
- #ifdef IDLE_DECOMMIT_ENABLED
- #if DBG_DUMP
- void
- IdleDecommitPageAllocator::DumpStats() const
- {
- __super::DumpStats();
- Output::Print(_u(" Idle Decommit Count : %4d\n"),
- this->idleDecommitCount);
- }
- #endif
- #endif
|