зеркало из https://github.com/mozilla/gecko-dev.git
don't process timers after wakeup (r=sfraser, sr=bryner, bug 197863)
This commit is contained in:
Родитель
7a84beea65
Коммит
fdc2d7dc32
|
@ -39,15 +39,17 @@
|
||||||
#include "nsAutoLock.h"
|
#include "nsAutoLock.h"
|
||||||
#include "pratom.h"
|
#include "pratom.h"
|
||||||
|
|
||||||
|
#include "nsIObserverService.h"
|
||||||
#include "nsIServiceManager.h"
|
#include "nsIServiceManager.h"
|
||||||
|
|
||||||
NS_IMPL_THREADSAFE_ISUPPORTS1(TimerThread, nsIRunnable)
|
NS_IMPL_THREADSAFE_ISUPPORTS3(TimerThread, nsIRunnable, nsISupportsWeakReference, nsIObserver)
|
||||||
|
|
||||||
TimerThread::TimerThread() :
|
TimerThread::TimerThread() :
|
||||||
mLock(nsnull),
|
mLock(nsnull),
|
||||||
mCondVar(nsnull),
|
mCondVar(nsnull),
|
||||||
mShutdown(PR_FALSE),
|
mShutdown(PR_FALSE),
|
||||||
mWaiting(PR_FALSE),
|
mWaiting(PR_FALSE),
|
||||||
|
mSleeping(PR_FALSE),
|
||||||
mDelayLineCounter(0),
|
mDelayLineCounter(0),
|
||||||
mMinTimerPeriod(0),
|
mMinTimerPeriod(0),
|
||||||
mTimeoutAdjustment(0)
|
mTimeoutAdjustment(0)
|
||||||
|
@ -68,6 +70,13 @@ TimerThread::~TimerThread()
|
||||||
nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[n]);
|
nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[n]);
|
||||||
NS_RELEASE(timer);
|
NS_RELEASE(timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
|
||||||
|
if (observerService) {
|
||||||
|
observerService->RemoveObserver(this, "sleep_notification");
|
||||||
|
observerService->RemoveObserver(this, "wake_notification");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult TimerThread::Init()
|
nsresult TimerThread::Init()
|
||||||
|
@ -95,7 +104,16 @@ nsresult TimerThread::Init()
|
||||||
PR_JOINABLE_THREAD,
|
PR_JOINABLE_THREAD,
|
||||||
PR_PRIORITY_NORMAL,
|
PR_PRIORITY_NORMAL,
|
||||||
PR_GLOBAL_THREAD);
|
PR_GLOBAL_THREAD);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
observerService->AddObserver(this, "sleep_notification", PR_TRUE);
|
||||||
|
observerService->AddObserver(this, "wake_notification", PR_TRUE);
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,78 +203,83 @@ NS_IMETHODIMP TimerThread::Run()
|
||||||
nsAutoLock lock(mLock);
|
nsAutoLock lock(mLock);
|
||||||
|
|
||||||
while (!mShutdown) {
|
while (!mShutdown) {
|
||||||
PRIntervalTime now = PR_IntervalNow();
|
PRIntervalTime waitFor;
|
||||||
nsTimerImpl *timer = nsnull;
|
|
||||||
|
if (mSleeping)
|
||||||
|
waitFor = PR_MillisecondsToInterval(100); // sleep for 0.1 seconds while not firing timers
|
||||||
|
else {
|
||||||
|
waitFor = PR_INTERVAL_NO_TIMEOUT;
|
||||||
|
PRIntervalTime now = PR_IntervalNow();
|
||||||
|
nsTimerImpl *timer = nsnull;
|
||||||
|
|
||||||
if (mTimers.Count() > 0) {
|
if (mTimers.Count() > 0) {
|
||||||
timer = NS_STATIC_CAST(nsTimerImpl*, mTimers[0]);
|
timer = NS_STATIC_CAST(nsTimerImpl*, mTimers[0]);
|
||||||
|
|
||||||
if (!TIMER_LESS_THAN(now, timer->mTimeout + mTimeoutAdjustment)) {
|
if (!TIMER_LESS_THAN(now, timer->mTimeout + mTimeoutAdjustment)) {
|
||||||
next:
|
next:
|
||||||
// NB: AddRef before the Release under RemoveTimerInternal to avoid
|
// NB: AddRef before the Release under RemoveTimerInternal to avoid
|
||||||
// mRefCnt passing through zero, in case all other refs than the one
|
// mRefCnt passing through zero, in case all other refs than the one
|
||||||
// from mTimers have gone away (the last non-mTimers[i]-ref's Release
|
// from mTimers have gone away (the last non-mTimers[i]-ref's Release
|
||||||
// must be racing with us, blocked in gThread->RemoveTimer waiting
|
// must be racing with us, blocked in gThread->RemoveTimer waiting
|
||||||
// for TimerThread::mLock, under nsTimerImpl::Release.
|
// for TimerThread::mLock, under nsTimerImpl::Release.
|
||||||
|
|
||||||
NS_ADDREF(timer);
|
NS_ADDREF(timer);
|
||||||
RemoveTimerInternal(timer);
|
RemoveTimerInternal(timer);
|
||||||
|
|
||||||
// We release mLock around the Fire call, of course, to avoid deadlock.
|
// We release mLock around the Fire call, of course, to avoid deadlock.
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
#ifdef DEBUG_TIMERS
|
#ifdef DEBUG_TIMERS
|
||||||
if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
||||||
PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
||||||
("Timer thread woke up %dms from when it was supposed to\n",
|
("Timer thread woke up %dms from when it was supposed to\n",
|
||||||
(now >= timer->mTimeout)
|
(now >= timer->mTimeout)
|
||||||
? PR_IntervalToMilliseconds(now - timer->mTimeout)
|
? PR_IntervalToMilliseconds(now - timer->mTimeout)
|
||||||
: -(PRInt32)PR_IntervalToMilliseconds(timer->mTimeout - now))
|
: -(PRInt32)PR_IntervalToMilliseconds(timer->mTimeout - now))
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We are going to let the call to PostTimerEvent here handle the release of the
|
||||||
|
// timer so that we don't end up releasing the timer on the TimerThread
|
||||||
|
// instead of on the thread it targets.
|
||||||
|
timer->PostTimerEvent();
|
||||||
|
timer = nsnull;
|
||||||
|
|
||||||
|
lock.lock();
|
||||||
|
if (mShutdown)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Update now, as PostTimerEvent plus the locking may have taken a tick or two,
|
||||||
|
// and we may goto next below.
|
||||||
|
now = PR_IntervalNow();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// We are going to let the call to PostTimerEvent here handle the release of the
|
|
||||||
// timer so that we don't end up releasing the timer on the TimerThread
|
|
||||||
// instead of on the thread it targets.
|
|
||||||
timer->PostTimerEvent();
|
|
||||||
timer = nsnull;
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
if (mShutdown)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Update now, as PostTimerEvent plus the locking may have taken a tick or two,
|
|
||||||
// and we may goto next below.
|
|
||||||
now = PR_IntervalNow();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
PRIntervalTime waitFor = PR_INTERVAL_NO_TIMEOUT;
|
if (mTimers.Count() > 0) {
|
||||||
|
timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[0]);
|
||||||
|
|
||||||
if (mTimers.Count() > 0) {
|
PRIntervalTime timeout = timer->mTimeout + mTimeoutAdjustment;
|
||||||
timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[0]);
|
|
||||||
|
|
||||||
PRIntervalTime timeout = timer->mTimeout + mTimeoutAdjustment;
|
// Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer is
|
||||||
|
// due now or overdue.
|
||||||
// Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer is
|
if (!TIMER_LESS_THAN(now, timeout))
|
||||||
// due now or overdue.
|
goto next;
|
||||||
if (!TIMER_LESS_THAN(now, timeout))
|
waitFor = timeout - now;
|
||||||
goto next;
|
}
|
||||||
waitFor = timeout - now;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_TIMERS
|
#ifdef DEBUG_TIMERS
|
||||||
if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
||||||
if (waitFor == PR_INTERVAL_NO_TIMEOUT)
|
if (waitFor == PR_INTERVAL_NO_TIMEOUT)
|
||||||
PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
||||||
("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
|
("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
|
||||||
else
|
else
|
||||||
PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
||||||
("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
|
("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
mWaiting = PR_TRUE;
|
mWaiting = PR_TRUE;
|
||||||
PR_WaitCondVar(mCondVar, waitFor);
|
PR_WaitCondVar(mCondVar, waitFor);
|
||||||
mWaiting = PR_FALSE;
|
mWaiting = PR_FALSE;
|
||||||
|
@ -352,3 +375,38 @@ PRBool TimerThread::RemoveTimerInternal(nsTimerImpl *aTimer)
|
||||||
NS_RELEASE(aTimer);
|
NS_RELEASE(aTimer);
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TimerThread::DoBeforeSleep()
|
||||||
|
{
|
||||||
|
mSleeping = PR_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerThread::DoAfterSleep()
|
||||||
|
{
|
||||||
|
for (PRInt32 i = 0; i < mTimers.Count(); i ++)
|
||||||
|
{
|
||||||
|
nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl*, mTimers[i]);
|
||||||
|
// get and set the delay to cause its timeout to be recomputed
|
||||||
|
PRUint32 delay;
|
||||||
|
timer->GetDelay(&delay);
|
||||||
|
timer->SetDelay(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nuke the stored adjustments, so they get recalibrated
|
||||||
|
mTimeoutAdjustment = 0;
|
||||||
|
mDelayLineCounter = 0;
|
||||||
|
mSleeping = PR_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
TimerThread::Observe(nsISupports* /* aSubject */, const char *aTopic, const PRUnichar* /* aData */)
|
||||||
|
{
|
||||||
|
if (strcmp(aTopic, "sleep_notification") == 0)
|
||||||
|
DoBeforeSleep();
|
||||||
|
else if (strcmp(aTopic, "wake_notification") == 0)
|
||||||
|
DoAfterSleep();
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
#ifndef TimerThread_h___
|
#ifndef TimerThread_h___
|
||||||
#define TimerThread_h___
|
#define TimerThread_h___
|
||||||
|
|
||||||
|
#include "nsWeakReference.h"
|
||||||
|
|
||||||
#include "nsIEventQueueService.h"
|
#include "nsIEventQueueService.h"
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
|
@ -49,7 +51,9 @@
|
||||||
#include "prinrval.h"
|
#include "prinrval.h"
|
||||||
#include "prlock.h"
|
#include "prlock.h"
|
||||||
|
|
||||||
class TimerThread : public nsIRunnable
|
class TimerThread : public nsSupportsWeakReference,
|
||||||
|
public nsIRunnable,
|
||||||
|
public nsIObserver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TimerThread();
|
TimerThread();
|
||||||
|
@ -57,7 +61,8 @@ public:
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIRUNNABLE
|
NS_DECL_NSIRUNNABLE
|
||||||
|
NS_DECL_NSIOBSERVER
|
||||||
|
|
||||||
nsresult Init();
|
nsresult Init();
|
||||||
nsresult Shutdown();
|
nsresult Shutdown();
|
||||||
|
|
||||||
|
@ -74,6 +79,9 @@ public:
|
||||||
// For use by nsTimerImpl::Fire()
|
// For use by nsTimerImpl::Fire()
|
||||||
nsCOMPtr<nsIEventQueueService> mEventQueueService;
|
nsCOMPtr<nsIEventQueueService> mEventQueueService;
|
||||||
|
|
||||||
|
void DoBeforeSleep();
|
||||||
|
void DoAfterSleep();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// These two internal helper methods must be called while mLock is held.
|
// These two internal helper methods must be called while mLock is held.
|
||||||
// AddTimerInternal returns the position where the timer was added in the
|
// AddTimerInternal returns the position where the timer was added in the
|
||||||
|
@ -87,7 +95,8 @@ private:
|
||||||
|
|
||||||
PRPackedBool mShutdown;
|
PRPackedBool mShutdown;
|
||||||
PRPackedBool mWaiting;
|
PRPackedBool mWaiting;
|
||||||
|
PRPackedBool mSleeping;
|
||||||
|
|
||||||
nsVoidArray mTimers;
|
nsVoidArray mTimers;
|
||||||
|
|
||||||
#define DELAY_LINE_LENGTH_LOG2 5
|
#define DELAY_LINE_LENGTH_LOG2 5
|
||||||
|
|
Загрузка…
Ссылка в новой задаче