don't process timers after wakeup (r=sfraser, sr=bryner, bug 197863)

This commit is contained in:
pinkerton%netscape.com 2003-10-31 02:31:13 +00:00
Родитель 7a84beea65
Коммит fdc2d7dc32
2 изменённых файлов: 129 добавлений и 62 удалений

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

@ -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