/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// This class is used to monitor low memory events delivered by Windows via // memory resource notification objects. When we enter a low memory scenario // the LowMemoryCallback() is invoked by Windows. This initial call triggers // an nsITimer that polls to see when the low memory condition has been lifted. // When it has, we'll stop polling and start waiting for the next // LowMemoryCallback(). Meanwhile, the polling may be stopped and restarted by // user-interaction events from the observer service. class nsAvailableMemoryWatcher final : public nsITimerCallback, public nsINamed, public nsAvailableMemoryWatcherBase { public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOBSERVER
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
// Indicates whether to start a timer when user interaction is notified. // This flag is needed because the low-memory callback may be triggered when // the user is inactive and we want to delay-start the timer. bool mNeedToRestartTimerOnUserInteracting MOZ_GUARDED_BY(mMutex); // Indicate that the available commit space is low. The timer handler needs // this flag because it is triggered by the low physical memory regardless // of the available commit space. bool mUnderMemoryPressure MOZ_GUARDED_BY(mMutex);
nsAvailableMemoryWatcher::~nsAvailableMemoryWatcher() { // These handles should have been released during the shutdown phase.
MOZ_ASSERT(!mLowMemoryHandle);
MOZ_ASSERT(!mWaitHandle);
}
// The |watcher| was addref'ed when we registered the wait handle in // ListenForLowMemory(). It is decremented when exiting the function, // so please make sure we unregister the wait handle after this line.
RefPtr<nsAvailableMemoryWatcher> watcher =
already_AddRefed<nsAvailableMemoryWatcher>( static_cast<nsAvailableMemoryWatcher*>(aContext));
MutexAutoLock lock(watcher->mMutex); if (watcher->mIsShutdown) { // mWaitHandle should have been unregistered during shutdown
MOZ_ASSERT(!watcher->mWaitHandle); return;
}
// On Windows, memory allocations fails when the available commit space is // not sufficient. It's possible that this callback function is invoked // but there is still commit space enough for the application to continue // to run. In such a case, there is no strong need to trigger the memory // pressure event. So we trigger the event only when the available commit // space is low. if (IsCommitSpaceLow()) {
watcher->OnLowMemory(lock);
} else { // Since we have unregistered the callback, we need to start a timer to // continue watching memory.
watcher->StartPollingIfUserInteracting(lock);
}
}
void nsAvailableMemoryWatcher::UnregisterMemoryResourceHandler( const MutexAutoLock&) { if (mWaitHandle) { bool res = ::UnregisterWait(mWaitHandle); if (res || ::GetLastError() != ERROR_IO_PENDING) { // We decrement the refcount only when we're sure the LowMemoryCallback() // callback won't be invoked, otherwise the callback will do it
this->Release();
}
mWaitHandle = nullptr;
}
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
UnregisterMemoryResourceHandler(aLock);
}
bool nsAvailableMemoryWatcher::ListenForLowMemory(const MutexAutoLock&) { if (mLowMemoryHandle && !mWaitHandle) { // We're giving ownership of this object to the LowMemoryCallback(). We // increment the count here so that the object is kept alive until the // callback decrements it.
this->AddRef(); bool res = ::RegisterWaitForSingleObject(
&mWaitHandle, mLowMemoryHandle, LowMemoryCallback, this, INFINITE,
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE); if (!res) { // We couldn't register the callback, decrement the count
this->Release();
} // Once we register the wait handle, we no longer need to start // the timer because we can continue watching memory via callback.
mNeedToRestartTimerOnUserInteracting = false; return res;
}
returnfalse;
}
void nsAvailableMemoryWatcher::MaybeSaveMemoryReport(const MutexAutoLock&) { if (mSavedReport) { return;
}
if (NS_IsMainThread()) {
MaybeSaveMemoryReport(aLock);
UpdateLowMemoryTimeStamp();
{ // Don't invoke UnloadTabAsync() with the lock to avoid deadlock // because nsAvailableMemoryWatcher::Notify may be invoked while // running the method.
MutexAutoUnlock unlock(mMutex);
mTabUnloader->UnloadTabAsync();
}
} else { // SaveMemoryReport and mTabUnloader needs to be run in the main thread // (See nsMemoryReporterManager::GetReportsForThisProcessExtended)
NS_DispatchToMainThread(NS_NewRunnableFunction( "nsAvailableMemoryWatcher::OnLowMemory", [self = RefPtr{this}]() {
{
MutexAutoLock lock(self->mMutex);
self->MaybeSaveMemoryReport(lock);
self->UpdateLowMemoryTimeStamp();
}
self->mTabUnloader->UnloadTabAsync();
}));
}
if (mUnderMemoryPressure) {
RecordTelemetryEventOnHighMemory(aLock);
NS_NotifyOfEventualMemoryPressure(MemoryPressureState::NoPressure);
}
mUnderMemoryPressure = false;
mSavedReport = false; // Will save a new report if memory gets low again
StopPolling(aLock);
ListenForLowMemory(aLock);
}
// static bool nsAvailableMemoryWatcher::IsCommitSpaceLow() { // Other options to get the available page file size: // - GetPerformanceInfo // Too slow, don't use it. // - PdhCollectQueryData and PdhGetRawCounterValue // Faster than GetPerformanceInfo, but slower than GlobalMemoryStatusEx. // - NtQuerySystemInformation(SystemMemoryUsageInformation) // Faster than GlobalMemoryStatusEx, but undocumented.
MEMORYSTATUSEX memStatus = {sizeof(memStatus)}; if (!::GlobalMemoryStatusEx(&memStatus)) { returnfalse;
}
void nsAvailableMemoryWatcher::StartPollingIfUserInteracting( const MutexAutoLock&) { // When the user is inactive, we mark the flag to delay-start // the timer when the user becomes active later.
mNeedToRestartTimerOnUserInteracting = true;
if (mInteracting && !mPolling) { if (NS_SUCCEEDED(mTimer->InitWithCallback( this, mPollingInterval, nsITimer::TYPE_REPEATING_SLACK))) {
mPolling = true;
}
}
}
// Timer callback, polls the low memory resource notification to detect when // we've freed enough memory or if we have to send more memory pressure events. // Polling stops automatically when the user is not interacting with the UI.
NS_IMETHODIMP
nsAvailableMemoryWatcher::Notify(nsITimer* aTimer) {
MutexAutoLock lock(mMutex);
StopPollingIfUserIdle(lock);
if (IsCommitSpaceLow()) {
OnLowMemory(lock);
} else {
OnHighMemory(lock);
}
// Observer service callback, used to stop the polling timer when the user // stops interacting with Firefox and resuming it when they interact again. // Also used to shut down the service if the application is quitting.
NS_IMETHODIMP
nsAvailableMemoryWatcher::Observe(nsISupports* aSubject, constchar* aTopic, const char16_t* aData) {
nsresult rv = nsAvailableMemoryWatcherBase::Observe(aSubject, aTopic, aData); if (NS_FAILED(rv)) { return rv;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.