diff options
Diffstat (limited to 'dom/media/systemservices/LoadManager.cpp')
-rw-r--r-- | dom/media/systemservices/LoadManager.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/dom/media/systemservices/LoadManager.cpp b/dom/media/systemservices/LoadManager.cpp new file mode 100644 index 000000000..f0f4f83a7 --- /dev/null +++ b/dom/media/systemservices/LoadManager.cpp @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "LoadManager.h" +#include "LoadMonitor.h" +#include "nsString.h" +#include "mozilla/Logging.h" +#include "prtime.h" +#include "prinrval.h" +#include "prsystem.h" + +#include "nsString.h" +#include "nsThreadUtils.h" +#include "nsReadableUtils.h" +#include "nsIObserverService.h" +#include "mozilla/Telemetry.h" +#include "mozilla/ArrayUtils.h" + +// MOZ_LOG=LoadManager:5 +mozilla::LazyLogModule gLoadManagerLog("LoadManager"); +#undef LOG +#undef LOG_ENABLED +#define LOG(args) MOZ_LOG(gLoadManagerLog, mozilla::LogLevel::Debug, args) +#define LOG_ENABLED() MOZ_LOG_TEST(gLoadManagerLog, mozilla::LogLevel::Verbose) + +namespace mozilla { + +/* static */ StaticRefPtr<LoadManagerSingleton> LoadManagerSingleton::sSingleton; + +NS_IMPL_ISUPPORTS(LoadManagerSingleton, nsIObserver) + + +LoadManagerSingleton::LoadManagerSingleton(bool aEncoderOnly, + int aLoadMeasurementInterval, + int aAveragingMeasurements, + float aHighLoadThreshold, + float aLowLoadThreshold) + : mLock("LoadManager"), + mCurrentState(webrtc::kLoadNormal), + mOveruseActive(false), + mLoadSum(0.0f), + mLoadSumMeasurements(0), + mLoadMeasurementInterval(aLoadMeasurementInterval), + mAveragingMeasurements(aAveragingMeasurements), + mHighLoadThreshold(aHighLoadThreshold), + mLowLoadThreshold(aLowLoadThreshold) +{ + LOG(("LoadManager - Initializing (%dms x %d, %f, %f)", + mLoadMeasurementInterval, mAveragingMeasurements, + mHighLoadThreshold, mLowLoadThreshold)); + MOZ_ASSERT(mHighLoadThreshold > mLowLoadThreshold); + if (!aEncoderOnly) { + mLoadMonitor = new LoadMonitor(mLoadMeasurementInterval); + mLoadMonitor->Init(mLoadMonitor); + mLoadMonitor->SetLoadChangeCallback(this); + } + + mLastStateChange = TimeStamp::Now(); + for (auto &in_state : mTimeInState) { + in_state = 0; + } +} + +LoadManagerSingleton::~LoadManagerSingleton() +{ + LOG(("LoadManager: shutting down LoadMonitor")); + MOZ_ASSERT(!mLoadMonitor, "why wasn't the LoadMonitor shut down in xpcom-shutdown?"); + if (mLoadMonitor) { + mLoadMonitor->Shutdown(); + } +} + +nsresult +LoadManagerSingleton::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + NS_ASSERTION(NS_IsMainThread(), "Observer invoked off the main thread"); + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + + if (!strcmp(aTopic, "xpcom-shutdown")) { + obs->RemoveObserver(this, "xpcom-shutdown"); + { + MutexAutoLock lock(mLock); + mObservers.Clear(); + } + if (mLoadMonitor) { + mLoadMonitor->Shutdown(); + mLoadMonitor = nullptr; + } + + LOG(("Releasing LoadManager singleton and thread")); + // Note: won't be released immediately as the Observer has a ref to us + sSingleton = nullptr; + } + return NS_OK; +} + +void +LoadManagerSingleton::LoadChanged(float aSystemLoad, float aProcesLoad) +{ + MutexAutoLock lock(mLock); + // Update total load, and total amount of measured seconds. + mLoadSum += aSystemLoad; + mLoadSumMeasurements++; + + if (mLoadSumMeasurements >= mAveragingMeasurements) { + double averagedLoad = mLoadSum / (float)mLoadSumMeasurements; + + webrtc::CPULoadState newState = mCurrentState; + + if (mOveruseActive || averagedLoad > mHighLoadThreshold) { + LOG(("LoadManager - LoadStressed")); + newState = webrtc::kLoadStressed; + } else if (averagedLoad < mLowLoadThreshold) { + LOG(("LoadManager - LoadRelaxed")); + newState = webrtc::kLoadRelaxed; + } else { + LOG(("LoadManager - LoadNormal")); + newState = webrtc::kLoadNormal; + } + + if (newState != mCurrentState) { + LoadHasChanged(newState); + } + + mLoadSum = 0; + mLoadSumMeasurements = 0; + } +} + +void +LoadManagerSingleton::OveruseDetected() +{ + LOG(("LoadManager - Overuse Detected")); + MutexAutoLock lock(mLock); + mOveruseActive = true; + if (mCurrentState != webrtc::kLoadStressed) { + LoadHasChanged(webrtc::kLoadStressed); + } +} + +void +LoadManagerSingleton::NormalUsage() +{ + LOG(("LoadManager - Overuse finished")); + MutexAutoLock lock(mLock); + mOveruseActive = false; +} + +void +LoadManagerSingleton::LoadHasChanged(webrtc::CPULoadState aNewState) +{ + mLock.AssertCurrentThreadOwns(); + LOG(("LoadManager - Signaling LoadHasChanged from %d to %d to %d listeners", + mCurrentState, aNewState, mObservers.Length())); + + // Record how long we spent in this state for later Telemetry or display + TimeStamp now = TimeStamp::Now(); + mTimeInState[mCurrentState] += (now - mLastStateChange).ToMilliseconds(); + mLastStateChange = now; + + mCurrentState = aNewState; + for (size_t i = 0; i < mObservers.Length(); i++) { + mObservers.ElementAt(i)->onLoadStateChanged(mCurrentState); + } +} + +void +LoadManagerSingleton::AddObserver(webrtc::CPULoadStateObserver * aObserver) +{ + LOG(("LoadManager - Adding Observer")); + MutexAutoLock lock(mLock); + mObservers.AppendElement(aObserver); +} + +void +LoadManagerSingleton::RemoveObserver(webrtc::CPULoadStateObserver * aObserver) +{ + LOG(("LoadManager - Removing Observer")); + MutexAutoLock lock(mLock); + if (!mObservers.RemoveElement(aObserver)) { + LOG(("LoadManager - Element to remove not found")); + } + if (mObservers.Length() == 0) { + // Record how long we spent in the final state for later Telemetry or display + TimeStamp now = TimeStamp::Now(); + mTimeInState[mCurrentState] += (now - mLastStateChange).ToMilliseconds(); + + float total = 0; + for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mTimeInState); i++) { + total += mTimeInState[i]; + } + // Don't include short calls; we don't have reasonable load data, and + // such short calls rarely reach a stable state. Keep relatively + // short calls separate from longer ones + bool log = total > 5*PR_MSEC_PER_SEC; + bool small = log && total < 30*PR_MSEC_PER_SEC; + if (log) { + // Note: We don't care about rounding here; thus total may be < 100 + Telemetry::Accumulate(small ? Telemetry::WEBRTC_LOAD_STATE_RELAXED_SHORT : + Telemetry::WEBRTC_LOAD_STATE_RELAXED, + (uint32_t) (mTimeInState[webrtc::CPULoadState::kLoadRelaxed]/total * 100)); + Telemetry::Accumulate(small ? Telemetry::WEBRTC_LOAD_STATE_NORMAL_SHORT : + Telemetry::WEBRTC_LOAD_STATE_NORMAL, + (uint32_t) (mTimeInState[webrtc::CPULoadState::kLoadNormal]/total * 100)); + Telemetry::Accumulate(small ? Telemetry::WEBRTC_LOAD_STATE_STRESSED_SHORT : + Telemetry::WEBRTC_LOAD_STATE_STRESSED, + (uint32_t) (mTimeInState[webrtc::CPULoadState::kLoadStressed]/total * 100)); + } + for (auto &in_state : mTimeInState) { + in_state = 0; + } + + if (mLoadMonitor) { + // Dance to avoid deadlock on mLock! + RefPtr<LoadMonitor> loadMonitor = mLoadMonitor.forget(); + MutexAutoUnlock unlock(mLock); + + loadMonitor->Shutdown(); + } + } +} + + +} |