nsresult nsTimerImpl::PostTimerEvent() { // XXX we may want to reuse this nsTimerEvent in the case of repeating timers. // Since TimerThread addref'd 'this' for us, we don't need to addref here. // We will release in destroyMyEvent. We need to copy the generation number // from this timer into the event, so we can avoid firing a timer that was // re-initialized after being canceled. nsRefPtr<nsTimerEvent> event = new nsTimerEvent(this, mGeneration); if (!event) return NS_ERROR_OUT_OF_MEMORY; #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { event->mInitTime = PR_IntervalNow(); } #endif // If this is a repeating precise timer, we need to calculate the time for // the next timer to fire before we make the callback. if (mType == TYPE_REPEATING_PRECISE) { SetDelayInternal(mDelay); if (gThread) { nsresult rv = gThread->AddTimer(this); if (NS_FAILED(rv)) return rv; } } nsresult rv = mCallingThread->Dispatch(event, NS_DISPATCH_NORMAL); if (NS_FAILED(rv) && gThread) gThread->RemoveTimer(this); return rv; }
nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay) { nsresult rv; NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED); rv = gThread->Init(); NS_ENSURE_SUCCESS(rv, rv); /** * In case of re-Init, both with and without a preceding Cancel, clear the * mCanceled flag and assign a new mGeneration. But first, remove any armed * timer from the timer thread's list. * * If we are racing with the timer thread to remove this timer and we lose, * the RemoveTimer call made here will fail to find this timer in the timer * thread's list, and will return false harmlessly. We test mArmed here to * avoid the small overhead in RemoveTimer of locking the timer thread and * checking its list for this timer. It's safe to test mArmed even though * it might be cleared on another thread in the next cycle (or even already * be cleared by another CPU whose store hasn't reached our CPU's cache), * because RemoveTimer is idempotent. */ if (mArmed) gThread->RemoveTimer(this); mCanceled = PR_FALSE; mGeneration = PR_AtomicIncrement(&gGenerator); mType = (PRUint8)aType; SetDelayInternal(aDelay); return gThread->AddTimer(this); }
NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay) { // If we're already repeating precisely, update mTimeout now so that the // new delay takes effect in the future. if (mTimeout != 0 && mType == TYPE_REPEATING_PRECISE) mTimeout = PR_IntervalNow(); SetDelayInternal(aDelay); if (!mFiring && gThread) gThread->TimerDelayChanged(this); return NS_OK; }
NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay) { if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) { // This may happen if someone tries to re-use a one-shot timer // by re-setting delay instead of reinitializing the timer. NS_ERROR("nsITimer->SetDelay() called when the " "one-shot timer is not set up."); return NS_ERROR_NOT_INITIALIZED; } // If we're already repeating precisely, update mTimeout now so that the // new delay takes effect in the future. if (mTimeout != 0 && mType == TYPE_REPEATING_PRECISE) mTimeout = PR_IntervalNow(); SetDelayInternal(aDelay); if (!mFiring && gThread) gThread->TimerDelayChanged(this); return NS_OK; }
void nsTimerImpl::Fire() { if (mCanceled) return; PRIntervalTime now = PR_IntervalNow(); #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { PRIntervalTime a = now - mStart; // actual delay in intervals PRUint32 b = PR_MillisecondsToInterval(mDelay); // expected delay in intervals PRUint32 d = PR_IntervalToMilliseconds((a > b) ? a - b : b - a); // delta in ms sDeltaSum += d; sDeltaSumSquared += double(d) * double(d); sDeltaNum++; PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] expected delay time %4dms\n", this, mDelay)); PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] actual delay time %4dms\n", this, PR_IntervalToMilliseconds(a))); PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] (mType is %d) -------\n", this, mType)); PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] delta %4dms\n", this, (a > b) ? (PRInt32)d : -(PRInt32)d)); mStart = mStart2; mStart2 = 0; } #endif PRIntervalTime timeout = mTimeout; if (mType == TYPE_REPEATING_PRECISE) { // Precise repeating timers advance mTimeout by mDelay without fail before // calling Fire(). timeout -= PR_MillisecondsToInterval(mDelay); } if (gThread) gThread->UpdateFilter(mDelay, timeout, now); if (mCallbackType == CALLBACK_TYPE_INTERFACE) mTimerCallbackWhileFiring = mCallback.i; mFiring = PR_TRUE; // Handle callbacks that re-init the timer, but avoid leaking. // See bug 330128. CallbackUnion callback = mCallback; PRUintn callbackType = mCallbackType; if (callbackType == CALLBACK_TYPE_INTERFACE) NS_ADDREF(callback.i); else if (callbackType == CALLBACK_TYPE_OBSERVER) NS_ADDREF(callback.o); ReleaseCallback(); switch (callbackType) { case CALLBACK_TYPE_FUNC: callback.c(this, mClosure); break; case CALLBACK_TYPE_INTERFACE: callback.i->Notify(this); break; case CALLBACK_TYPE_OBSERVER: callback.o->Observe(static_cast<nsITimer*>(this), NS_TIMER_CALLBACK_TOPIC, nsnull); break; default:; } // If the callback didn't re-init the timer, and it's not a one-shot timer, // restore the callback state. if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType != TYPE_ONE_SHOT && !mCanceled) { mCallback = callback; mCallbackType = callbackType; } else { // The timer was a one-shot, or the callback was reinitialized. if (callbackType == CALLBACK_TYPE_INTERFACE) NS_RELEASE(callback.i); else if (callbackType == CALLBACK_TYPE_OBSERVER) NS_RELEASE(callback.o); } mFiring = PR_FALSE; mTimerCallbackWhileFiring = nsnull; #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] Took %dms to fire timer callback\n", this, PR_IntervalToMilliseconds(PR_IntervalNow() - now))); } #endif if (mType == TYPE_REPEATING_SLACK) { SetDelayInternal(mDelay); // force mTimeout to be recomputed. if (gThread) gThread->AddTimer(this); } }