Bug 720493 - nsIdleService sometimes fails to restart its idle time detection timer. r=bbondy

This commit is contained in:
Mike Kristoffersen 2012-02-28 18:40:38 -05:00
Родитель 00a3c3851b
Коммит 728c4964ae
2 изменённых файлов: 339 добавлений и 267 удалений

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

@ -24,7 +24,7 @@
* Contributor(s):
* Gijs Kruitbosch <gijskruitbosch@gmail.com>
* Edward Lee <edward.lee@engineering.uiuc.edu>
* Mike Kristoffersen <moz@mikek.dk>
* Mike Kristoffersen <mozstuff@mikek.dk>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -56,9 +56,11 @@ using namespace mozilla;
#define OBSERVER_TOPIC_IDLE "idle"
#define OBSERVER_TOPIC_BACK "back"
#define OBSERVER_TOPIC_IDLE_DAILY "idle-daily"
// interval in seconds between internal idle time requests.
#define MIN_IDLE_POLL_INTERVAL 5
#define MAX_IDLE_POLL_INTERVAL 300 /* 5 min */
// interval in milliseconds between internal idle time requests.
#define MIN_IDLE_POLL_INTERVAL_MSEC (5 * PR_MSEC_PER_SEC) /* 5 sec */
// Time used by the daily idle serivce to determine a significant idle time.
#define DAILY_SIGNIFICANT_IDLE_SERVICE_SEC 300 /* 5 min */
// Pref for last time (seconds since epoch) daily notification was sent.
#define PREF_LAST_DAILY "idle.lastDailyNotification"
// Number of seconds in a day.
@ -100,14 +102,17 @@ nsIdleServiceDaily::Observe(nsISupports *,
}
// Stop observing idle for today.
(void)mIdleService->RemoveIdleObserver(this, MAX_IDLE_POLL_INTERVAL);
(void)mIdleService->RemoveIdleObserver(this,
DAILY_SIGNIFICANT_IDLE_SERVICE_SEC);
// Set the last idle-daily time pref.
PRInt32 nowSec = static_cast<PRInt32>(PR_Now() / PR_USEC_PER_SEC);
Preferences::SetInt(PREF_LAST_DAILY, nowSec);
// Start timer for the next check in one day.
(void)mTimer->InitWithFuncCallback(DailyCallback, this, SECONDS_PER_DAY * 1000,
(void)mTimer->InitWithFuncCallback(DailyCallback,
this,
SECONDS_PER_DAY * PR_MSEC_PER_SEC,
nsITimer::TYPE_ONE_SHOT);
return NS_OK;
@ -139,7 +144,9 @@ nsIdleServiceDaily::Init()
}
else {
// Start timer for the next check in one day.
(void)mTimer->InitWithFuncCallback(DailyCallback, this, SECONDS_PER_DAY * 1000,
(void)mTimer->InitWithFuncCallback(DailyCallback,
this,
SECONDS_PER_DAY * PR_MSEC_PER_SEC,
nsITimer::TYPE_ONE_SHOT);
}
}
@ -160,15 +167,88 @@ nsIdleServiceDaily::DailyCallback(nsITimer* aTimer, void* aClosure)
// The one thing we do every day is to start waiting for the user to "have
// a significant idle time".
(void)me->mIdleService->AddIdleObserver(me, MAX_IDLE_POLL_INTERVAL);
(void)me->mIdleService->AddIdleObserver(me,
DAILY_SIGNIFICANT_IDLE_SERVICE_SEC);
}
/**
* The idle services goal is to notify subscribers when a certain time has
* passed since the last user interaction with the system.
*
* On some platforms this is defined as the last time user events reached this
* application, on other platforms it is a system wide thing - the preferred
* implementation is to use the system idle time, rather than the application
* idle time, as the things depending on the idle service are likely to use
* significant resources (network, disk, memory, cpu, etc.).
*
* When the idle service needs to use the system wide idle timer, it typically
* needs to poll the idle time value by the means of a timer. It needs to
* poll fast when it is in active idle mode (when it has a listener in the idle
* mode) as it needs to detect if the user is active in other applications.
*
* When the service is waiting for the first listener to become idle, or when
* it is only monitoring application idle time, it only needs to have the timer
* expire at the time the next listener goes idle.
*
* The core state of the service is determined by:
*
* - A list of listeners.
*
* - A boolean that tells if any listeners are in idle mode.
*
* - A delta value that indicates when, measured from the last non-idle time,
* the next listener should switch to idle mode.
*
* - An absolute time of the last time idle mode was detected (this is used to
* judge if we have been out of idle mode since the last invocation of the
* service.
*
* There are four entry points into the system:
*
* - A new listener is registered.
*
* - An existing listener is deregistered.
*
* - User interaction is detected.
*
* - The timer expires.
*
* When a new listener is added its idle timeout, is compared with the next idle
* timeout, and if lower, that time is stored as the new timeout, and the timer
* is reconfigured to ensure a timeout around the time the new listener should
* timeout.
*
* If the next idle time is above the idle time requested by the new listener
* it won't be informed until the timer expires, this is to avoid recursive
* behavior and to simplify the code. In this case the timer will be set to
* about 10 ms.
*
* When an existing listener is deregistered, it is just removed from the list
* of active listeners, we don't stop the timer, we just let it expire.
*
* When user interaction is detected, either because it was directly detected or
* because we polled the system timer and found it to be unexpected low, then we
* check the flag that tells us if any listeners are in idle mode, if there are
* they are removed from idle mode and told so, and we reset our state
* caculating the next timeout and restart the timer if needed.
*
* ---- Build in logic
*
* In order to avoid restarting the timer endlessly, the timer function has
* logic that will only restart the timer, if the requested timeout is before
* the current timeout.
*
*/
////////////////////////////////////////////////////////////////////////////////
//// nsIdleService
nsIdleService::nsIdleService() : mLastIdleReset(0)
, mLastHandledActivity(0)
, mPolledIdleTimeIsValid(false)
nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAtInPR(0),
mAnyObserverIdle(false),
mDeltaToNextIdleSwitchInS(PR_UINT32_MAX),
mLastUserInteractionInPR(0)
{
mDailyIdle = new nsIdleServiceDaily(this);
mDailyIdle->Init();
@ -176,17 +256,21 @@ nsIdleService::nsIdleService() : mLastIdleReset(0)
nsIdleService::~nsIdleService()
{
StopTimer();
if(mTimer) {
mTimer->Cancel();
}
}
NS_IMETHODIMP
nsIdleService::AddIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTime)
nsIdleService::AddIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTimeInS)
{
NS_ENSURE_ARG_POINTER(aObserver);
NS_ENSURE_ARG(aIdleTime);
// We don't accept idle time at 0, and we can't handle idle time that are too
// high either - no more than ~136 years.
NS_ENSURE_ARG_RANGE(aIdleTimeInS, 1, (PR_UINT32_MAX / 10) - 1);
// Put the time + observer in a struct we can keep:
IdleListener listener(aObserver, aIdleTime);
IdleListener listener(aObserver, aIdleTimeInS);
if (!mArrayListeners.AppendElement(listener)) {
return NS_ERROR_OUT_OF_MEMORY;
@ -199,25 +283,33 @@ nsIdleService::AddIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTime)
NS_ENSURE_SUCCESS(rv, rv);
}
// Make sure our observer goes into 'idle' immediately if applicable.
CheckAwayState(true);
// Check if the newly added observer has a smaller wait time than what we
// are wating for now.
if (mDeltaToNextIdleSwitchInS > aIdleTimeInS) {
// If it is, then this is the next to move to idle (at this point we
// don't care if it should have switched already).
mDeltaToNextIdleSwitchInS = aIdleTimeInS;
}
// Ensure timer is running.
ReconfigureTimer();
return NS_OK;
}
NS_IMETHODIMP
nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aTime)
nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aTimeInS)
{
NS_ENSURE_ARG_POINTER(aObserver);
NS_ENSURE_ARG(aTime);
IdleListener listener(aObserver, aTime);
// Find the entry and remove it:
NS_ENSURE_ARG_POINTER(aObserver);
NS_ENSURE_ARG(aTimeInS);
IdleListener listener(aObserver, aTimeInS);
// Find the entry and remove it, if it was the last entry, we just let the
// existing timer run to completion (there might be a new registration in a
// little while.
IdleListenerComparator c;
if (mArrayListeners.RemoveElement(listener, c)) {
if (mArrayListeners.IsEmpty()) {
StopTimer();
}
return NS_OK;
}
@ -226,13 +318,63 @@ nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aTime)
}
void
nsIdleService::ResetIdleTimeOut()
nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS)
{
mLastIdleReset = PR_IntervalToSeconds(PR_IntervalNow());
if (!mLastIdleReset) mLastIdleReset = 1;
// Store the time
mLastUserInteractionInPR = PR_Now() - (idleDeltaInMS * PR_USEC_PER_MSEC);
// If no one is idle, then we are done, any existing timers can keep running.
if (!mAnyObserverIdle) {
return;
}
// Mark all idle services as non-idle, and calculate the next idle timeout.
nsCOMArray<nsIObserver> notifyList;
mDeltaToNextIdleSwitchInS = PR_UINT32_MAX;
// Loop trough all listeners, and find any that have detected idle.
for (PRUint32 i = 0; i < mArrayListeners.Length(); i++) {
IdleListener& curListener = mArrayListeners.ElementAt(i);
// If the listener was idle, then he shouldn't be any longer.
if (curListener.isIdle) {
notifyList.AppendObject(curListener.observer);
curListener.isIdle = false;
}
// Check if the listener is the next one to timeout.
mDeltaToNextIdleSwitchInS = PR_MIN(mDeltaToNextIdleSwitchInS,
curListener.reqIdleTime);
}
// When we are done, then we wont have anyone idle.
mAnyObserverIdle = false;
// Restart the idle timer, and do so before anyone can delay us.
ReconfigureTimer();
PRInt32 numberOfPendingNotifications = notifyList.Count();
// Bail if nothing to do.
if (!numberOfPendingNotifications) {
return;
}
// Now send "back" events to all, if any should have timed out allready, then
// they will be reawaken by the timer that is already running.
// We need a text string to send with any state change events.
nsAutoString timeStr;
timeStr.AppendInt((PRInt32)(idleDeltaInMS / PR_MSEC_PER_SEC));
// Send the "non-idle" events.
while (numberOfPendingNotifications--) {
notifyList[numberOfPendingNotifications]->Observe(this,
OBSERVER_TOPIC_BACK,
timeStr.get());
}
// Now check if this changes anything
CheckAwayState(false);
}
NS_IMETHODIMP
@ -243,36 +385,36 @@ nsIdleService::GetIdleTime(PRUint32* idleTime)
return NS_ERROR_NULL_POINTER;
}
// Polled idle time in ms
// Polled idle time in ms.
PRUint32 polledIdleTimeMS;
mPolledIdleTimeIsValid = PollIdleTime(&polledIdleTimeMS);
bool polledIdleTimeIsValid = PollIdleTime(&polledIdleTimeMS);
// If we don't have any valid data, then we are not in idle - pr. definition.
if (!mPolledIdleTimeIsValid && 0 == mLastIdleReset) {
if (!polledIdleTimeIsValid && 0 == mLastUserInteractionInPR) {
*idleTime = 0;
return NS_OK;
}
// If we never got a reset, just return the pulled time.
if (0 == mLastIdleReset) {
if (0 == mLastUserInteractionInPR) {
*idleTime = polledIdleTimeMS;
return NS_OK;
}
// timeSinceReset is in seconds.
PRUint32 timeSinceReset =
PR_IntervalToSeconds(PR_IntervalNow()) - mLastIdleReset;
// timeSinceReset is in milliseconds.
PRUint32 timeSinceResetInMS = (PR_Now() - mLastUserInteractionInPR) /
PR_USEC_PER_MSEC;
// If we did't get pulled data, return the time since last idle reset.
if (!mPolledIdleTimeIsValid) {
if (!polledIdleTimeIsValid) {
// We need to convert to ms before returning the time.
*idleTime = timeSinceReset * 1000;
*idleTime = timeSinceResetInMS;
return NS_OK;
}
// Otherwise return the shortest time detected (in ms).
*idleTime = NS_MIN(timeSinceReset * 1000, polledIdleTimeMS);
*idleTime = NS_MIN(timeSinceResetInMS, polledIdleTimeMS);
return NS_OK;
}
@ -293,112 +435,54 @@ nsIdleService::UsePollMode()
}
void
nsIdleService::IdleTimerCallback(nsITimer* aTimer, void* aClosure)
nsIdleService::StaticIdleTimerCallback(nsITimer* aTimer, void* aClosure)
{
static_cast<nsIdleService*>(aClosure)->CheckAwayState(false);
static_cast<nsIdleService*>(aClosure)->IdleTimerCallback();
}
void
nsIdleService::CheckAwayState(bool aNewObserver)
nsIdleService::IdleTimerCallback(void)
{
/**
* Find our last detected idle time (it's important this happens before the
* call below to GetIdleTime, as we use the two values to detect if there
* has been user activity since the last time we were here).
*/
PRUint32 curTime = static_cast<PRUint32>(PR_Now() / PR_USEC_PER_SEC);
PRUint32 lastTime = curTime - mLastHandledActivity;
bool bootstrapTimer = mLastHandledActivity == 0;
// Remember that we no longer have a timer running.
mCurrentlySetToTimeoutAtInPR = 0;
/**
* Too short since last check.
* Bail out
*/
if (!lastTime && !aNewObserver) {
// Get the current idle time.
PRUint32 currentIdleTimeInMS;
if (NS_FAILED(GetIdleTime(&currentIdleTimeInMS))) {
return;
}
// Get the idle time (in seconds).
PRUint32 idleTime;
if (NS_FAILED(GetIdleTime(&idleTime))) {
// Check if we have had some user interaction we didn't handle previously
// we do the caluculation in ms to lessen the chance for rounding errors to
// trigger wrong results, it is also very important that we call PR_Now AFTER
// the call to GetIdleTime().
if (((PR_Now() - mLastUserInteractionInPR) / PR_USEC_PER_MSEC) >
currentIdleTimeInMS)
{
// We had user activity, so handle that part first (to ensure the listeners
// don't risk getting an non-idle after they get a new idle indication.
ResetIdleTimeOut(currentIdleTimeInMS);
// NOTE: We can't bail here, as we might have something already timed out.
}
// Find the idle time in S.
PRUint32 currentIdleTimeInS = currentIdleTimeInMS / PR_MSEC_PER_SEC;
// Restart timer and bail if no-one are expected to be in idle
if (mDeltaToNextIdleSwitchInS > currentIdleTimeInS) {
// If we didn't expect anyone to be idle, then just re-start the timer.
ReconfigureTimer();
return;
}
// If we have no valid data about the idle time, stop
if (!mPolledIdleTimeIsValid && 0 == mLastIdleReset) {
return;
}
// Tell expired listeners they are expired,and find the next timeout
// GetIdleTime returns the time in ms, internally we only calculate in s.
idleTime /= 1000;
// Set the time for last user activity.
mLastHandledActivity = curTime - idleTime;
/**
* Now, if the idle time, is less than what we expect, it means the
* user was active since last time that we checked.
*/
bool userActivity = lastTime > idleTime;
if (userActivity) {
if (TryNotifyBackState(idleTime) || idleTime) {
RescheduleIdleTimer(idleTime);
}
}
if (!userActivity || aNewObserver || bootstrapTimer) {
TryNotifyIdleState(idleTime);
RescheduleIdleTimer(idleTime);
}
}
bool
nsIdleService::TryNotifyBackState(PRUint32 aIdleTime)
{
nsCOMArray<nsIObserver> notifyList;
// Loop trough all listeners, and find any that have detected idle.
for (PRUint32 i = 0; i < mArrayListeners.Length(); i++) {
IdleListener& curListener = mArrayListeners.ElementAt(i);
if (curListener.isIdle) {
notifyList.AppendObject(curListener.observer);
curListener.isIdle = false;
}
}
PRInt32 numberOfPendingNotifications = notifyList.Count();
// Bail id nothing to do
if(!numberOfPendingNotifications) {
return false;
}
// We need a text string to send with any state change events.
nsAutoString timeStr;
timeStr.AppendInt(aIdleTime);
// Send the "non-idle" events.
while(numberOfPendingNotifications--) {
notifyList[numberOfPendingNotifications]->Observe(this,
OBSERVER_TOPIC_BACK,
timeStr.get());
}
// We found something so return true
return true;
}
bool
nsIdleService::TryNotifyIdleState(PRUint32 aIdleTime)
{
/**
* Now we need to check for listeners that have expired, and while we are
* looping through all the elements, we will also calculate when, if ever
* the next one will need to be notified.
*/
// We need to initialise the time to the next idle switch.
mDeltaToNextIdleSwitchInS = PR_UINT32_MAX;
// Create list of observers that should be notified.
nsCOMArray<nsIObserver> notifyList;
for (PRUint32 i = 0; i < mArrayListeners.Length(); i++) {
@ -407,106 +491,114 @@ nsIdleService::TryNotifyIdleState(PRUint32 aIdleTime)
// We are only interested in items, that are not in the idle state.
if (!curListener.isIdle) {
// If they have an idle time smaller than the actual idle time.
if (curListener.reqIdleTime <= aIdleTime) {
// then add the listener to the list of listeners that should be
if (curListener.reqIdleTime <= currentIdleTimeInS) {
// Then add the listener to the list of listeners that should be
// notified.
notifyList.AppendObject(curListener.observer);
// This listener is now idle.
curListener.isIdle = true;
} else {
// Listeners that are not timed out yet are candidates for timing out.
mDeltaToNextIdleSwitchInS = PR_MIN(mDeltaToNextIdleSwitchInS,
curListener.reqIdleTime);
}
}
}
// Restart the timer before any notifications that could slow us down are
// done.
ReconfigureTimer();
PRInt32 numberOfPendingNotifications = notifyList.Count();
// Bail if nothing to do
if(!numberOfPendingNotifications) {
return false;
// Bail if nothing to do.
if (!numberOfPendingNotifications) {
return;
}
// Remember we have someone idle.
mAnyObserverIdle = true;
// We need a text string to send with any state change events.
nsAutoString timeStr;
timeStr.AppendInt(aIdleTime);
timeStr.AppendInt(currentIdleTimeInS);
// Notify all listeners that just timed out.
while(numberOfPendingNotifications--) {
while (numberOfPendingNotifications--) {
notifyList[numberOfPendingNotifications]->Observe(this,
OBSERVER_TOPIC_IDLE,
timeStr.get());
}
return true;
}
void
nsIdleService::RescheduleIdleTimer(PRUint32 aIdleTime)
nsIdleService::SetTimerExpiryIfBefore(PRTime aNextTimeoutInPR)
{
/**
* Placet to store the wait time to the next notification, note that
* PR_UINT32_MAX means no-one are listening (or that they have such a big
* delay that it doesn't matter).
*/
PRUint32 nextWaitTime = PR_UINT32_MAX;
/**
* Place to remember if there are any listeners that are in the idle state,
* if there are, we need to poll frequently in a polling environment to detect
* when the user becomes active again.
*/
bool anyOneIdle = false;
for (PRUint32 i = 0; i < mArrayListeners.Length(); i++) {
IdleListener& curListener = mArrayListeners.ElementAt(i);
// We are only interested in items, that are not in the idle state.
if (!curListener.isIdle) {
// If it hasn't expired yet, then we should note the time when it should
// expire.
nextWaitTime = NS_MIN(nextWaitTime, curListener.reqIdleTime);
}
// Remember if anyone becomes idle (it's safe to do this as a binary compare
// as we are or'ing).
anyOneIdle |= curListener.isIdle;
// Bail if we don't have a timer service.
if (!mTimer) {
return;
}
// In order to find when the next idle event should time out, we need to
// subtract the time we should wait, from the time that has already passed.
if (PR_UINT32_MAX != nextWaitTime) {
nextWaitTime -= aIdleTime;
}
// If the new timeout is before the old one or we don't have a timer running,
// then restart the timer.
if (mCurrentlySetToTimeoutAtInPR > aNextTimeoutInPR ||
!mCurrentlySetToTimeoutAtInPR) {
// If we are in poll mode, we need to poll for activity if anyone are idle,
// otherwise we can wait polling until they would expire.
if (UsePollMode() &&
anyOneIdle &&
nextWaitTime > MIN_IDLE_POLL_INTERVAL) {
nextWaitTime = MIN_IDLE_POLL_INTERVAL;
}
mCurrentlySetToTimeoutAtInPR = aNextTimeoutInPR ;
// Start the timer if there is anything to wait for.
if (PR_UINT32_MAX != nextWaitTime) {
StartTimer(nextWaitTime);
}
}
void
nsIdleService::StartTimer(PRUint32 aDelay)
{
if (mTimer) {
StopTimer();
if (aDelay) {
mTimer->InitWithFuncCallback(IdleTimerCallback, this, aDelay*1000,
nsITimer::TYPE_ONE_SHOT);
}
}
}
void
nsIdleService::StopTimer()
{
if (mTimer) {
// Stop the current timer (it's ok to try'n stop it, even it isn't running).
mTimer->Cancel();
// Check that the timeout is actually in the future, otherwise make it so.
PRTime currentTimeInPR = PR_Now();
if (currentTimeInPR > mCurrentlySetToTimeoutAtInPR) {
mCurrentlySetToTimeoutAtInPR = currentTimeInPR;
}
// Add 10 ms to ensure we don't undershoot, and never get a "0" timer.
mCurrentlySetToTimeoutAtInPR += 10 * PR_USEC_PER_MSEC;
// Start the timer
mTimer->InitWithFuncCallback(StaticIdleTimerCallback,
this,
(mCurrentlySetToTimeoutAtInPR -
currentTimeInPR) / PR_USEC_PER_MSEC,
nsITimer::TYPE_ONE_SHOT);
}
}
void
nsIdleService::ReconfigureTimer(void)
{
// Check if either someone is idle, or someone will become idle.
if (!mAnyObserverIdle && PR_UINT32_MAX == mDeltaToNextIdleSwitchInS) {
// If not, just let any existing timers run to completion
// And bail out.
return;
}
// Find the next timeout value, assuming we are not polling.
// We need to store the current time, so we don't get artifacts from the time
// ticking while we are processing.
PRTime curTimeInPR = PR_Now();
PRTime nextTimeoutAtInPR = mLastUserInteractionInPR +
(((PRTime)mDeltaToNextIdleSwitchInS) *
PR_USEC_PER_SEC);
// Check if we should correct the timeout time because we should poll before.
if (mAnyObserverIdle && UsePollMode()) {
PRTime pollTimeout = curTimeInPR +
MIN_IDLE_POLL_INTERVAL_MSEC * PR_USEC_PER_SEC;
if (nextTimeoutAtInPR > pollTimeout) {
nextTimeoutAtInPR = pollTimeout;
}
}
SetTimerExpiryIfBefore(nextTimeoutAtInPR);
}

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

@ -23,7 +23,7 @@
*
* Contributor(s):
* Gijs Kruitbosch <gijskruitbosch@gmail.com>
* Mike Kristoffersen <moz@mikek.dk>
* Mike Kristoffersen <mozstuff@mikek.dk>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -128,10 +128,17 @@ public:
NS_IMETHOD RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTime);
NS_IMETHOD GetIdleTime(PRUint32* idleTime);
void ResetIdleTimeOut();
/**
* Function that resets the idle time in the service, in other words it
* sets the time for the last user interaction to now, or now-idleDelta
*
* @param idleDelta the time (in milliseconds) since the last user inter
* action
**/
void ResetIdleTimeOut(PRUint32 idleDeltaInMS = 0);
protected:
~nsIdleService();
virtual ~nsIdleService();
/**
* If there is a platform specific function to poll the system idel time
@ -158,29 +165,21 @@ protected:
*/
virtual bool UsePollMode();
/**
* Send expired events and start timers.
*
* @param aNewObserver
* Whether there are new observers to check.
*/
void CheckAwayState(bool aNewObserver);
private:
/**
* Start the internal timer, restart it if it is allready running.
* Ensure that the timer is expiring at least at the given time
*
* @param aDelay
* The time in seconds that should pass before the next timeout.
* The function might not restart the timer if there is one running currently
*
* @param aNextTimeoutInPR
* The last absolute time the timer should expire
*/
void StartTimer(PRUint32 aDelay);
void SetTimerExpiryIfBefore(PRTime aNextTimeoutInPR);
/**
* Stop the internal timer, it is safe to call this function, even when
* there are no timers running.
* Stores the next timeout time, 0 means timer not running
*/
void StopTimer();
PRTime mCurrentlySetToTimeoutAtInPR;
/**
* mTimer holds the internal timer used by this class to detect when to poll
@ -199,62 +198,43 @@ private:
nsRefPtr<nsIdleServiceDaily> mDailyIdle;
/**
* Contains the time of the last idle reset or 0 if there haven't been a
* reset.
* <p>
* Time is kept in seconds since the epoch at midnight, January 1, 1970 UTC.
* Boolean indicating if any observers are in idle mode
*/
PRUint32 mLastIdleReset;
bool mAnyObserverIdle;
/**
* The time since the last handled activity (which might be different than
* mLastIdleReset, since the activity that reset the idle timer could just
* have happend, and not handled yet).
* <p>
* Time is kept in seconds since the epoch at midnight, January 1, 1970 UTC.
* Delta time from last non idle time to when the next observer should switch
* to idle mode
*
* Time in seconds
*
* If this value is 0 it means there are no active observers
*/
PRUint32 mLastHandledActivity;
PRUint32 mDeltaToNextIdleSwitchInS;
/**
* Absolute value for when the last user interaction took place.
*/
PRTime mLastUserInteractionInPR;
/**
* Function that ensures the timer is running with at least the minimum time
* needed. It will kill the timer if there are no active observers.
*/
void ReconfigureTimer(void);
/**
* Callback function that is called when the internal timer expires.
*
* This in turn calls the IdleTimerCallback that does the real processing
*/
static void IdleTimerCallback(nsITimer* aTimer, void* aClosure);
static void StaticIdleTimerCallback(nsITimer* aTimer, void* aClosure);
/**
* Whether the idle time calculated in the last call to GetIdleTime is
* actually valid (see nsIdleService.idl - we return 0 when it isn't).
* Function that handles when a timer has expired
*/
bool mPolledIdleTimeIsValid;
/**
* Check if any listeners were in idle state, notify them that user
* is back and change them to non-idle state.
*
* @param aIdleTime
* The idle time in seconds.
*
* @return true if any listeners were notified.
*/
bool TryNotifyBackState(PRUint32 aIdleTime);
/**
* Check if any listeners were in non-idle state, notify them that user
* is idle and change them to idle state.
*
* @param aIdleTime
* The idle time in seconds.
*
* @return true if any listeners were notified.
*/
bool TryNotifyIdleState(PRUint32 aIdleTime);
/**
* Reschedule the idle timer to fire on next checkpoint.
*
* @param aIdleTime
* The idle time has passed in seconds.
*/
void RescheduleIdleTimer(PRUint32 aIdleTime);
void IdleTimerCallback(void);
};
#endif // nsIdleService_h__