/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "nsTimerPeriodical.h" #include "nsCOMPtr.h" #include "prlog.h" // this is also in the windows code. Prolly needs to be in nsITimer.h #define NS_PRIORITY_IMMEDIATE NS_PRIORITY_HIGHEST nsTimerPeriodical * nsTimerPeriodical::gPeriodical = nsnull; //---------------------------------------------------------------------------------------- nsTimerPeriodical * nsTimerPeriodical::GetPeriodical() //---------------------------------------------------------------------------------------- { if (gPeriodical == NULL) gPeriodical = new nsTimerPeriodical(); return gPeriodical; } #pragma mark - //---------------------------------------------------------------------------------------- nsTimerPeriodical::nsTimerPeriodical() //---------------------------------------------------------------------------------------- : mTimers(nsnull) , mReadyTimers(nsnull) , mFiringTimer(nsnull) { } //---------------------------------------------------------------------------------------- nsTimerPeriodical::~nsTimerPeriodical() //---------------------------------------------------------------------------------------- { PR_ASSERT(!mTimers && !mReadyTimers); } #pragma mark - //---------------------------------------------------------------------------------------- nsresult nsTimerPeriodical::AddTimer(nsTimerImpl * aTimer) //---------------------------------------------------------------------------------------- { NS_ADDREF(aTimer); // we hold a ref to the timer until it fires AddTimerToList(aTimer, mTimers); if (mTimers) StartRepeating(); return NS_OK; } //---------------------------------------------------------------------------------------- nsresult nsTimerPeriodical::RemoveTimer(nsTimerImpl * aTimer) //---------------------------------------------------------------------------------------- { // if someone tries to cancel a timer that is firing now, we just ignore them. if (aTimer == mFiringTimer) return NS_OK; RemoveTimerFromList(aTimer, mTimers); RemoveTimerFromList(aTimer, mReadyTimers); if (aTimer && !aTimer->mTimerSpent) { // if we haven't fired the timer, release it aTimer->mTimerSpent = PR_TRUE; NS_RELEASE(aTimer); } return NS_OK; } // Called through every event loop // Loops through the list of available timers, and // fires off the appropriate ones //---------------------------------------------------------------------------------------- void nsTimerPeriodical::RepeatAction(const EventRecord &inMacEvent) //---------------------------------------------------------------------------------------- { ProcessTimers(inMacEvent.when); if (!mTimers) StopRepeating(); // ProcessTimers can put timers in the ready queue, so start idling // if we have any if (mReadyTimers) StartIdling(); } //---------------------------------------------------------------------------------------- void nsTimerPeriodical::IdleAction(const EventRecord &inMacEvent) //---------------------------------------------------------------------------------------- { FireNextReadyTimer(); if (!mReadyTimers) StopIdling(); } #pragma mark - //---------------------------------------------------------------------------------------- void nsTimerPeriodical::ProcessTimers(UInt32 currentTicks) // This only fires one timer per call right now. //---------------------------------------------------------------------------------------- { PRBool done = PR_FALSE; while (!done) { nsTimerImpl* curTimer = mTimers; while (curTimer) { NS_ASSERTION(curTimer->IsGoodTimer(), "Bad timer!"); nsTimerImpl* nextTimer = curTimer->mNext; if (curTimer->GetFireTime() <= currentTicks) { ProcessExpiredTimer(curTimer); // this changes the list break; } curTimer = nextTimer; } done = (curTimer == nsnull); // we got to the end } } //---------------------------------------------------------------------------------------- void nsTimerPeriodical::FireAndReprimeTimer(nsTimerImpl* aTimer) //---------------------------------------------------------------------------------------- { nsCOMPtr kungFuDeathGrip(aTimer); // if this timer is a precise timer, set the next fire time // before we execute the callback if (aTimer->GetType() == NS_TYPE_REPEATING_PRECISE) aTimer->SetDelay(aTimer->GetDelay()); mFiringTimer = aTimer; aTimer->Fire(); mFiringTimer = nsnull; // if this is a slack timer, set the delay now if (aTimer->GetType() == NS_TYPE_REPEATING_SLACK) aTimer->SetDelay(aTimer->GetDelay()); // if this is a repeating timer, put it back in the list if (aTimer->GetType() != NS_TYPE_ONE_SHOT) { AddTimerToList(aTimer, mTimers); } else { if (!aTimer->mTimerSpent) { aTimer->mTimerSpent = PR_TRUE; NS_RELEASE(aTimer); // this timer is dead } } } //---------------------------------------------------------------------------------------- void nsTimerPeriodical::FireNextReadyTimer() //---------------------------------------------------------------------------------------- { if (mReadyTimers) { nsTimerImpl* aTimer = mReadyTimers; RemoveTimerFromList(aTimer, mReadyTimers); FireAndReprimeTimer(aTimer); } } //---------------------------------------------------------------------------------------- void nsTimerPeriodical::AddToReadyQueue(nsTimerImpl* aTimer) //---------------------------------------------------------------------------------------- { PRUint32 priority = aTimer->GetPriority(); NS_ASSERTION(aTimer && priority < NS_PRIORITY_IMMEDIATE, "Should have non-immediate timer here"); // do an insertion sort by priority if (mReadyTimers) { if (priority > mReadyTimers->GetPriority()) { mReadyTimers->mPrev = aTimer; aTimer->mNext = mReadyTimers; mReadyTimers = aTimer; } else { nsTimerImpl *readyTimer = mReadyTimers; nsTimerImpl *prevTimer; // we know we will enter the while loop at least the first // time, and thus prevt will be initialized while (readyTimer && (readyTimer->GetPriority() >= priority)) { NS_ASSERTION(readyTimer->IsGoodTimer(), "Bad timer!"); prevTimer = readyTimer; readyTimer = readyTimer->mNext; } aTimer->mPrev = prevTimer; aTimer->mNext = prevTimer->mNext; prevTimer->mNext = aTimer; if (aTimer->mNext) aTimer->mNext->mPrev = aTimer; } } else { mReadyTimers = aTimer; } } //---------------------------------------------------------------------------------------- void nsTimerPeriodical::ProcessExpiredTimer(nsTimerImpl* aTimer) //---------------------------------------------------------------------------------------- { RemoveTimerFromList(aTimer, mTimers); if (aTimer->GetPriority() >= NS_PRIORITY_IMMEDIATE) { FireAndReprimeTimer(aTimer); } else { // add to the ready queue AddToReadyQueue(aTimer); } } #pragma mark - //---------------------------------------------------------------------------------------- nsresult nsTimerPeriodical::AddTimerToList(nsTimerImpl* aTimer, nsTimerImpl*& timerList) //---------------------------------------------------------------------------------------- { // make sure it's not already there RemoveTimerFromList(aTimer, timerList); // keep list sorted by fire time if (timerList) { if (aTimer->GetFireTime() < timerList->GetFireTime()) { timerList->mPrev = aTimer; aTimer->mNext = timerList; timerList = aTimer; } else { nsTimerImpl *t = timerList; nsTimerImpl *prevt; // we know we will enter the while loop at least the first // time, and thus prevt will be initialized while (t && (t->GetFireTime() <= aTimer->GetFireTime())) { prevt = t; t = t->mNext; } aTimer->mPrev = prevt; aTimer->mNext = prevt->mNext; prevt->mNext = aTimer; if (aTimer->mNext) aTimer->mNext->mPrev = aTimer; } } else { timerList = aTimer; } return NS_OK; } //---------------------------------------------------------------------------------------- nsresult nsTimerPeriodical::RemoveTimerFromList(nsTimerImpl* aTimer, nsTimerImpl*& timerList) //---------------------------------------------------------------------------------------- { nsTimerImpl* t = timerList; nsTimerImpl* next_t = nsnull; if (t) next_t = t->mNext; while (t) { if (t == aTimer) { if (timerList == t) timerList = t->mNext; if (t->mPrev) t->mPrev->mNext = t->mNext; if (t->mNext) t->mNext->mPrev = t->mPrev; t->mNext = nsnull; t->mPrev = nsnull; } t = next_t; if (t) next_t = t->mNext; } return NS_OK; }