Bug 1311425 - Make idle callbacks aware of nsITimers, r=froydnj

--HG--
extra : rebase_source : 3baa3054c1ca08783fd0d04dab91d3f97d2e65f1
This commit is contained in:
Andreas Farre 2017-05-24 21:12:55 -04:00
Родитель 0833711613
Коммит 8e2322bcce
9 изменённых файлов: 121 добавлений и 9 удалений

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/Preferences.h"
#include "nsRefreshDriver.h"
#include "nsThreadUtils.h"
#define DEFAULT_LONG_IDLE_PERIOD 50.0f
#define DEFAULT_MIN_IDLE_PERIOD 3.0f
@ -26,15 +27,18 @@ MainThreadIdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
now + TimeDuration::FromMilliseconds(GetLongIdlePeriod());
currentGuess = nsRefreshDriver::GetIdleDeadlineHint(currentGuess);
currentGuess = NS_GetTimerDeadlineHintOnCurrentThread(currentGuess);
// If the idle period is too small, then just return a null time
// to indicate we are busy. Otherwise return the actual deadline.
TimeDuration minIdlePeriod =
TimeDuration::FromMilliseconds(GetMinIdlePeriod());
bool busySoon = currentGuess.IsNull() ||
(now >= (currentGuess - minIdlePeriod)) ||
currentGuess < mLastIdleDeadline;
if (!busySoon) {
*aIdleDeadline = currentGuess;
*aIdleDeadline = mLastIdleDeadline = currentGuess;
}
return NS_OK;

Просмотреть файл

@ -17,10 +17,17 @@ class MainThreadIdlePeriod final : public IdlePeriod
public:
NS_DECL_NSIIDLEPERIOD
MainThreadIdlePeriod()
: mLastIdleDeadline(TimeStamp::Now())
{
}
static float GetLongIdlePeriod();
static float GetMinIdlePeriod();
private:
virtual ~MainThreadIdlePeriod() {}
TimeStamp mLastIdleDeadline;
};
} // namespace mozilla

Просмотреть файл

@ -589,6 +589,43 @@ TimerThread::RemoveTimer(nsTimerImpl* aTimer)
return NS_OK;
}
TimeStamp
TimerThread::FindNextFireTimeForCurrentThread(TimeStamp aDefault)
{
MonitorAutoLock lock(mMonitor);
TimeStamp timeStamp = aDefault;
for (auto timers = mTimers.begin(); timers != mTimers.end(); ++timers) {
nsTimerImpl* timer = (*timers)->Value();
if (!timer) {
continue;
}
if (timer->mTimeout > aDefault) {
break;
}
// Don't yield to timers created with the *_LOW_PRIORITY type.
if (timer->IsLowPriority()) {
continue;
}
bool isOnCurrentThread = false;
nsresult rv = timer->mEventTarget->IsOnCurrentThread(&isOnCurrentThread);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
if (isOnCurrentThread) {
timeStamp = timer->mTimeout;
break;
}
}
return timeStamp;
}
// This function must be called from within a lock
bool
TimerThread::AddTimerInternal(nsTimerImpl* aTimer)

Просмотреть файл

@ -46,6 +46,7 @@ public:
nsresult AddTimer(nsTimerImpl* aTimer);
nsresult RemoveTimer(nsTimerImpl* aTimer);
TimeStamp FindNextFireTimeForCurrentThread(TimeStamp );
void DoBeforeSleep();
void DoAfterSleep();

Просмотреть файл

@ -114,6 +114,22 @@ interface nsITimer : nsISupports
*/
const short TYPE_REPEATING_PRECISE_CAN_SKIP = 3;
/**
* Same as TYPE_REPEATING_SLACK with the exception that idle events
* won't yield to timers with this type. Use this when you want an
* idle callback to be scheduled to run even though this timer is
* about to fire.
*/
const short TYPE_REPEATING_SLACK_LOW_PRIORITY = 4;
/**
* Same as TYPE_ONE_SHOT with the exception that idle events won't
* yield to timers with this type. Use this when you want an idle
* callback to be scheduled to run even though this timer is about
* to fire.
*/
const short TYPE_ONE_SHOT_LOW_PRIORITY = 5;
/**
* Initialize a timer that will fire after the said delay.
* A user must keep a reference to this timer till it is
@ -244,4 +260,3 @@ interface nsITimer : nsISupports
#define NS_TIMER_CONTRACTID "@mozilla.org/timer;1"
#define NS_TIMER_CALLBACK_TOPIC "timer-callback"
%}

Просмотреть файл

@ -1153,8 +1153,20 @@ nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
MOZ_ASSERT(aEvent);
if (!mIdleEvents.HasPendingEvent(aProofOfLock)) {
aEvent = nullptr;
return;
}
TimeStamp idleDeadline;
mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
{
// Releasing the lock temporarily since getting the idle period
// might need to lock the timer thread. Unlocking here might make
// us receive an event on the main queue, but we've committed to
// run an idle event anyhow.
MutexAutoUnlock unlock(mLock);
mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
}
if (!idleDeadline || idleDeadline < TimeStamp::Now()) {
aEvent = nullptr;

Просмотреть файл

@ -1767,6 +1767,20 @@ private:
void
NS_SetMainThread();
/**
* Return the expiration time of the next timer to run on the current
* thread. If that expiration time is greater than aDefault, then
* return aDefault.
*
* Timers with either the type nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY or
* nsITIMER::TYPE_REPEATING_SLACK_LOW_PRIORITY will be skipped when
* searching for the next expiration time. This enables timers to
* have lower priority than callbacks dispatched from
* nsIThread::IdleDispatch.
*/
extern mozilla::TimeStamp
NS_GetTimerDeadlineHintOnCurrentThread(mozilla::TimeStamp aDefault);
namespace mozilla {
/**

Просмотреть файл

@ -44,6 +44,14 @@ GetTimerLog()
return sTimerLog;
}
TimeStamp
NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault)
{
return gThread
? gThread->FindNextFireTimeForCurrentThread(aDefault)
: TimeStamp();
}
// This module prints info about which timers are firing, which is useful for
// wakeups for the purposes of power profiling. Set the following environment
// variable before starting the browser.
@ -505,7 +513,7 @@ nsTimerImpl::Fire(int32_t aGeneration)
// Repeating timer has not been re-init or canceled; reschedule
mCallbackDuringFire.swap(mCallback);
TimeDuration delay = TimeDuration::FromMilliseconds(mDelay);
if (mType == nsITimer::TYPE_REPEATING_SLACK) {
if (IsSlack()) {
mTimeout = TimeStamp::Now() + delay;
} else {
mTimeout = mTimeout + delay;
@ -537,10 +545,12 @@ nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay
{
const char* typeStr;
switch (aType) {
case nsITimer::TYPE_ONE_SHOT: typeStr = "ONE_SHOT"; break;
case nsITimer::TYPE_REPEATING_SLACK: typeStr = "SLACK "; break;
case nsITimer::TYPE_ONE_SHOT: typeStr = "ONE_SHOT "; break;
case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY: typeStr = "ONE_LOW "; break;
case nsITimer::TYPE_REPEATING_SLACK: typeStr = "SLACK "; break;
case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY: typeStr = "SLACK_LOW "; break;
case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP: typeStr = "PRECISE "; break;
case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP: typeStr = "PRECISE "; break;
default: MOZ_CRASH("bad type");
}
@ -714,4 +724,3 @@ nsTimerImpl::GetTracedTask()
}
#endif

Просмотреть файл

@ -152,7 +152,20 @@ public:
nsITimer::TYPE_REPEATING_PRECISE <
nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
"invalid ordering of timer types!");
return mType >= nsITimer::TYPE_REPEATING_SLACK;
return mType >= nsITimer::TYPE_REPEATING_SLACK &&
mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY;
}
bool IsLowPriority() const
{
return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY ||
mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
}
bool IsSlack() const
{
return mType == nsITimer::TYPE_REPEATING_SLACK ||
mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
}
void GetName(nsACString& aName);