forked from mirrors/gecko-dev
Bug 1826224: Enable high-precision timers on Windows for foreground processes when not on battery power r=smaug
Initially controlled by a pref and only enabled by default in Nightly. Differential Revision: https://phabricator.services.mozilla.com/D189555
This commit is contained in:
parent
380463fef9
commit
4a6beb6b60
3 changed files with 119 additions and 3 deletions
|
|
@ -14423,6 +14423,19 @@
|
|||
value: 10000.0
|
||||
mirror: always
|
||||
|
||||
#ifdef XP_WIN
|
||||
# Controls whether or not TimerThread will automatically increase the Windows timer
|
||||
# resolution when appropriate conditions are met.
|
||||
- name: timer.auto_increase_timer_resolution
|
||||
type: RelaxedAtomicBool
|
||||
#ifdef NIGHTLY_BUILD
|
||||
value: true
|
||||
#else
|
||||
value: false
|
||||
#endif
|
||||
mirror: always
|
||||
#endif
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Prefs starting with "toolkit."
|
||||
#---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/ChaosMode.h"
|
||||
#include "mozilla/ArenaAllocator.h"
|
||||
|
|
@ -24,6 +25,44 @@
|
|||
|
||||
using namespace mozilla;
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Include Windows header required for enabling high-precision timers.
|
||||
# include <windows.h>
|
||||
# include <mmsystem.h>
|
||||
|
||||
static constexpr UINT kTimerPeriodHiRes = 1;
|
||||
static constexpr UINT kTimerPeriodLowRes = 16;
|
||||
|
||||
// Helper functions to determine what Windows timer resolution to target.
|
||||
static constexpr UINT GetDesiredTimerPeriod(const bool aOnBatteryPower,
|
||||
const bool aLowProcessPriority) {
|
||||
const bool useLowResTimer = aOnBatteryPower || aLowProcessPriority;
|
||||
return useLowResTimer ? kTimerPeriodLowRes : kTimerPeriodHiRes;
|
||||
}
|
||||
|
||||
static_assert(GetDesiredTimerPeriod(true, false) == kTimerPeriodLowRes);
|
||||
static_assert(GetDesiredTimerPeriod(false, true) == kTimerPeriodLowRes);
|
||||
static_assert(GetDesiredTimerPeriod(true, true) == kTimerPeriodLowRes);
|
||||
static_assert(GetDesiredTimerPeriod(false, false) == kTimerPeriodHiRes);
|
||||
|
||||
UINT TimerThread::ComputeDesiredTimerPeriod() const {
|
||||
const bool lowPriorityProcess =
|
||||
mCachedPriority.load(std::memory_order_relaxed) <
|
||||
hal::PROCESS_PRIORITY_FOREGROUND;
|
||||
|
||||
// NOTE: Using short-circuiting here to avoid call to GetSystemPowerStatus()
|
||||
// when we know that that result will not affect the final result. (As
|
||||
// confirmed by the static_assert's above, onBatteryPower does not affect the
|
||||
// result when the lowPriorityProcess is true.)
|
||||
SYSTEM_POWER_STATUS status;
|
||||
const bool onBatteryPower = !lowPriorityProcess &&
|
||||
GetSystemPowerStatus(&status) &&
|
||||
(status.ACLineStatus == 0);
|
||||
|
||||
return GetDesiredTimerPeriod(onBatteryPower, lowPriorityProcess);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Uncomment the following line to enable runtime stats during development.
|
||||
// #define TIMERS_RUNTIME_STATS
|
||||
|
||||
|
|
@ -175,6 +214,8 @@ TimerObserverRunnable::Run() {
|
|||
false);
|
||||
observerService->AddObserver(mObserver, "resume_process_notification",
|
||||
false);
|
||||
observerService->AddObserver(mObserver, "ipc:process-priority-changed",
|
||||
false);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -720,6 +761,25 @@ TimerThread::Run() {
|
|||
AutoTArray<uint64_t, kMaxQueuedTimerFired> queuedTimersFiredPerWakeup;
|
||||
queuedTimersFiredPerWakeup.SetLengthAndRetainStorage(kMaxQueuedTimerFired);
|
||||
|
||||
#ifdef XP_WIN
|
||||
// kTimerPeriodEvalIntervalSec is the minimum amount of time that must pass
|
||||
// before we will consider changing the timer period again.
|
||||
static constexpr float kTimerPeriodEvalIntervalSec = 2.0f;
|
||||
const TimeDuration timerPeriodEvalInterval =
|
||||
TimeDuration::FromSeconds(kTimerPeriodEvalIntervalSec);
|
||||
TimeStamp nextTimerPeriodEval = TimeStamp::Now() + timerPeriodEvalInterval;
|
||||
|
||||
// If this is false, we will perform all of the logic but will stop short of
|
||||
// actually changing the timer period.
|
||||
const bool adjustTimerPeriod =
|
||||
StaticPrefs::timer_auto_increase_timer_resolution();
|
||||
UINT lastTimePeriodSet = ComputeDesiredTimerPeriod();
|
||||
|
||||
if (adjustTimerPeriod) {
|
||||
timeBeginPeriod(lastTimePeriodSet);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t timersFiredThisWakeup = 0;
|
||||
while (!mShutdown) {
|
||||
// Have to use PRIntervalTime here, since PR_WaitCondVar takes it
|
||||
|
|
@ -742,6 +802,20 @@ TimerThread::Run() {
|
|||
waitFor = TimeDuration::Forever();
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
#ifdef XP_WIN
|
||||
if (now >= nextTimerPeriodEval) {
|
||||
const UINT newTimePeriod = ComputeDesiredTimerPeriod();
|
||||
if (newTimePeriod != lastTimePeriodSet) {
|
||||
if (adjustTimerPeriod) {
|
||||
timeEndPeriod(lastTimePeriodSet);
|
||||
timeBeginPeriod(newTimePeriod);
|
||||
}
|
||||
lastTimePeriodSet = newTimePeriod;
|
||||
}
|
||||
nextTimerPeriodEval = now + timerPeriodEvalInterval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TIMER_THREAD_STATISTICS
|
||||
if (!mNotified && !mIntendedWakeupTime.IsNull() &&
|
||||
now < mIntendedWakeupTime) {
|
||||
|
|
@ -915,6 +989,13 @@ TimerThread::Run() {
|
|||
queuedTimersFiredPerWakeup);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// About to shut down - let's finish off the last time period that we set.
|
||||
if (adjustTimerPeriod) {
|
||||
timeEndPeriod(lastTimePeriodSet);
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
@ -1284,8 +1365,18 @@ void TimerThread::DoAfterSleep() {
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
|
||||
const char16_t* /* aData */) {
|
||||
TimerThread::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
if (strcmp(aTopic, "ipc:process-priority-changed") == 0) {
|
||||
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
|
||||
MOZ_ASSERT(props != nullptr);
|
||||
|
||||
int32_t priority = static_cast<int32_t>(hal::PROCESS_PRIORITY_UNKNOWN);
|
||||
props->GetPropertyAsInt32(u"priority"_ns, &priority);
|
||||
mCachedPriority.store(static_cast<hal::ProcessPriority>(priority),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (StaticPrefs::timer_ignore_sleep_wake_notifications()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "nsTArray.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/HalTypes.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/ProfilerUtils.h"
|
||||
|
||||
|
|
@ -75,6 +76,14 @@ class TimerThread final : public mozilla::Runnable, public nsIObserver {
|
|||
void PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
|
||||
MOZ_REQUIRES(mMonitor);
|
||||
|
||||
// Using atomic because this value is written to in one place, and read from
|
||||
// in another, and those two locations are likely to be executed from separate
|
||||
// threads. Reads/writes to an aligned value this size should be atomic even
|
||||
// without using std::atomic, but doing this explicitly provides a good
|
||||
// reminder that this is accessed from multiple threads.
|
||||
std::atomic<mozilla::hal::ProcessPriority> mCachedPriority =
|
||||
mozilla::hal::PROCESS_PRIORITY_UNKNOWN;
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
// Lock ordering requirements:
|
||||
// (optional) ThreadWrapper::sMutex ->
|
||||
|
|
@ -171,6 +180,10 @@ class TimerThread final : public mozilla::Runnable, public nsIObserver {
|
|||
TimeDuration minDelay,
|
||||
TimeDuration maxDelay) const;
|
||||
|
||||
#ifdef XP_WIN
|
||||
UINT ComputeDesiredTimerPeriod() const;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
// Checks mTimers to see if any entries are out of order or any cached
|
||||
// timeouts are incorrect and will assert if any inconsistency is found. Has
|
||||
|
|
@ -231,5 +244,4 @@ class TimerThread final : public mozilla::Runnable, public nsIObserver {
|
|||
void PrintStatistics() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* TimerThread_h___ */
|
||||
|
|
|
|||
Loading…
Reference in a new issue