Back to index

lightning-sunbird  0.9+nobinonly
nsTimerImpl.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is mozilla.org code.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2001
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Stuart Parmenter <pavlov@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsTimerImpl.h"
00042 #include "TimerThread.h"
00043 #include "nsAutoLock.h"
00044 
00045 #include "nsVoidArray.h"
00046 
00047 #include "nsIEventQueue.h"
00048 
00049 #include "prmem.h"
00050 
00051 static PRInt32          gGenerator = 0;
00052 static TimerThread*     gThread = nsnull;
00053 static PRBool           gFireOnIdle = PR_FALSE;
00054 static nsTimerManager*  gManager = nsnull;
00055 
00056 #ifdef DEBUG_TIMERS
00057 #include <math.h>
00058 
00059 double nsTimerImpl::sDeltaSumSquared = 0;
00060 double nsTimerImpl::sDeltaSum = 0;
00061 double nsTimerImpl::sDeltaNum = 0;
00062 
00063 static void
00064 myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
00065                    double *meanResult, double *stdDevResult)
00066 {
00067   double mean = 0.0, var = 0.0, stdDev = 0.0;
00068   if (n > 0.0 && sumOfValues >= 0) {
00069     mean = sumOfValues / n;
00070     double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
00071     if (temp < 0.0 || n <= 1)
00072       var = 0.0;
00073     else
00074       var = temp / (n * (n - 1));
00075     // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
00076     stdDev = var != 0.0 ? sqrt(var) : 0.0;
00077   }
00078   *meanResult = mean;
00079   *stdDevResult = stdDev;
00080 }
00081 #endif
00082 
00083 NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsTimerImpl, nsITimer, nsITimerInternal)
00084 NS_IMPL_THREADSAFE_ADDREF(nsTimerImpl)
00085 
00086 NS_IMETHODIMP_(nsrefcnt) nsTimerImpl::Release(void)
00087 {
00088   nsrefcnt count;
00089 
00090   NS_PRECONDITION(0 != mRefCnt, "dup release");
00091   count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
00092   NS_LOG_RELEASE(this, count, "nsTimerImpl");
00093   if (count == 0) {
00094     mRefCnt = 1; /* stabilize */
00095 
00096     /* enable this to find non-threadsafe destructors: */
00097     /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
00098     NS_DELETEXPCOM(this);
00099     return 0;
00100   }
00101 
00102   // If only one reference remains, and mArmed is set, then the ref must be
00103   // from the TimerThread::mTimers array, so we Cancel this timer to remove
00104   // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
00105   //
00106   // We use an inlined version of nsTimerImpl::Cancel here to check for the
00107   // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
00108   // timer is not found in the mTimers array -- i.e., when the timer was not
00109   // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
00110   // being true here.  That can happen if the armed timer is being fired by
00111   // TimerThread::Run as we race and test mArmed just before it is cleared by
00112   // the timer thread.  If the RemoveTimer call below doesn't find this timer
00113   // in the mTimers array, then the last ref to this timer is held manually
00114   // and temporarily by the TimerThread, so we should fall through to the
00115   // final return and return 1, not 0.
00116   //
00117   // The original version of this thread-based timer code kept weak refs from
00118   // TimerThread::mTimers, removing this timer's weak ref in the destructor,
00119   // but that leads to double-destructions in the race described above, and
00120   // adding mArmed doesn't help, because destructors can't be deferred, once
00121   // begun.  But by combining reference-counting and a specialized Release
00122   // method with "is this timer still in the mTimers array once we acquire
00123   // the TimerThread's lock" testing, we defer destruction until we're sure
00124   // that only one thread has its hot little hands on this timer.
00125   //
00126   // Note that both approaches preclude a timer creator, and everyone else
00127   // except the TimerThread who might have a strong ref, from dropping all
00128   // their strong refs without implicitly canceling the timer.  Timers need
00129   // non-mTimers-element strong refs to stay alive.
00130 
00131   if (count == 1 && mArmed) {
00132     mCanceled = PR_TRUE;
00133 
00134     if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
00135       return 0;
00136   }
00137 
00138   return count;
00139 }
00140 
00141 nsTimerImpl::nsTimerImpl() :
00142   mClosure(nsnull),
00143   mCallbackType(CALLBACK_TYPE_UNKNOWN),
00144   mIdle(PR_TRUE),
00145   mFiring(PR_FALSE),
00146   mArmed(PR_FALSE),
00147   mCanceled(PR_FALSE),
00148   mGeneration(0),
00149   mDelay(0),
00150   mTimeout(0)
00151 {
00152   // XXXbsmedberg: shouldn't this be in Init()?
00153   nsIThread::GetCurrent(getter_AddRefs(mCallingThread));
00154 
00155   mCallback.c = nsnull;
00156 
00157 #ifdef DEBUG_TIMERS
00158   mStart = 0;
00159   mStart2 = 0;
00160 #endif
00161 }
00162 
00163 nsTimerImpl::~nsTimerImpl()
00164 {
00165   ReleaseCallback();
00166 }
00167 
00168 //static
00169 nsresult
00170 nsTimerImpl::Startup()
00171 {
00172   nsresult rv;
00173 
00174   gThread = new TimerThread();
00175   if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
00176 
00177   NS_ADDREF(gThread);
00178   rv = gThread->InitLocks();
00179 
00180   if (NS_FAILED(rv)) {
00181     NS_RELEASE(gThread);
00182   }
00183 
00184   return rv;
00185 }
00186 
00187 void nsTimerImpl::Shutdown()
00188 {
00189 #ifdef DEBUG_TIMERS
00190   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
00191     double mean = 0, stddev = 0;
00192     myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
00193 
00194     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
00195     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
00196   }
00197 #endif
00198 
00199   if (!gThread)
00200     return;
00201 
00202   gThread->Shutdown();
00203   NS_RELEASE(gThread);
00204 
00205   gFireOnIdle = PR_FALSE;
00206 }
00207 
00208 
00209 nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay)
00210 {
00211   nsresult rv;
00212 
00213   NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED);
00214 
00215   rv = gThread->Init();
00216   NS_ENSURE_SUCCESS(rv, rv);
00217 
00232   if (mArmed)
00233     gThread->RemoveTimer(this);
00234   mCanceled = PR_FALSE;
00235   mGeneration = PR_AtomicIncrement(&gGenerator);
00236 
00237   mType = (PRUint8)aType;
00238   SetDelayInternal(aDelay);
00239 
00240   return gThread->AddTimer(this);
00241 }
00242 
00243 NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
00244                                                 void *aClosure,
00245                                                 PRUint32 aDelay,
00246                                                 PRUint32 aType)
00247 {
00248   ReleaseCallback();
00249   mCallbackType = CALLBACK_TYPE_FUNC;
00250   mCallback.c = aFunc;
00251   mClosure = aClosure;
00252 
00253   return InitCommon(aType, aDelay);
00254 }
00255 
00256 NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
00257                                             PRUint32 aDelay,
00258                                             PRUint32 aType)
00259 {
00260   ReleaseCallback();
00261   mCallbackType = CALLBACK_TYPE_INTERFACE;
00262   mCallback.i = aCallback;
00263   NS_ADDREF(mCallback.i);
00264 
00265   return InitCommon(aType, aDelay);
00266 }
00267 
00268 NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
00269                                 PRUint32 aDelay,
00270                                 PRUint32 aType)
00271 {
00272   ReleaseCallback();
00273   mCallbackType = CALLBACK_TYPE_OBSERVER;
00274   mCallback.o = aObserver;
00275   NS_ADDREF(mCallback.o);
00276 
00277   return InitCommon(aType, aDelay);
00278 }
00279 
00280 NS_IMETHODIMP nsTimerImpl::Cancel()
00281 {
00282   mCanceled = PR_TRUE;
00283 
00284   if (gThread)
00285     gThread->RemoveTimer(this);
00286 
00287   return NS_OK;
00288 }
00289 
00290 NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay)
00291 {
00292   // If we're already repeating precisely, update mTimeout now so that the
00293   // new delay takes effect in the future.
00294   if (mTimeout != 0 && mType == TYPE_REPEATING_PRECISE)
00295     mTimeout = PR_IntervalNow();
00296 
00297   SetDelayInternal(aDelay);
00298 
00299   if (!mFiring && gThread)
00300     gThread->TimerDelayChanged(this);
00301 
00302   return NS_OK;
00303 }
00304 
00305 NS_IMETHODIMP nsTimerImpl::GetDelay(PRUint32* aDelay)
00306 {
00307   *aDelay = mDelay;
00308   return NS_OK;
00309 }
00310 
00311 NS_IMETHODIMP nsTimerImpl::SetType(PRUint32 aType)
00312 {
00313   mType = (PRUint8)aType;
00314   // XXX if this is called, we should change the actual type.. this could effect
00315   // repeating timers.  we need to ensure in Fire() that if mType has changed
00316   // during the callback that we don't end up with the timer in the queue twice.
00317   return NS_OK;
00318 }
00319 
00320 NS_IMETHODIMP nsTimerImpl::GetType(PRUint32* aType)
00321 {
00322   *aType = mType;
00323   return NS_OK;
00324 }
00325 
00326 
00327 NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
00328 {
00329   *aClosure = mClosure;
00330   return NS_OK;
00331 }
00332 
00333 
00334 NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
00335 {
00336   if (mCallbackType == CALLBACK_TYPE_INTERFACE)
00337     NS_IF_ADDREF(*aCallback = mCallback.i);
00338   else
00339     *aCallback = nsnull;
00340 
00341   return NS_OK;
00342 }
00343 
00344 
00345 NS_IMETHODIMP nsTimerImpl::GetIdle(PRBool *aIdle)
00346 {
00347   *aIdle = mIdle;
00348   return NS_OK;
00349 }
00350 
00351 NS_IMETHODIMP nsTimerImpl::SetIdle(PRBool aIdle)
00352 {
00353   mIdle = aIdle;
00354   return NS_OK;
00355 }
00356 
00357 void nsTimerImpl::Fire()
00358 {
00359   if (mCanceled)
00360     return;
00361 
00362   PRIntervalTime now = PR_IntervalNow();
00363 #ifdef DEBUG_TIMERS
00364   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
00365     PRIntervalTime a = now - mStart; // actual delay in intervals
00366     PRUint32       b = PR_MillisecondsToInterval(mDelay); // expected delay in intervals
00367     PRUint32       d = PR_IntervalToMilliseconds((a > b) ? a - b : b - a); // delta in ms
00368     sDeltaSum += d;
00369     sDeltaSumSquared += double(d) * double(d);
00370     sDeltaNum++;
00371 
00372     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] expected delay time %4dms\n", this, mDelay));
00373     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] actual delay time   %4dms\n", this, PR_IntervalToMilliseconds(a)));
00374     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] (mType is %d)       -------\n", this, mType));
00375     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p]     delta           %4dms\n", this, (a > b) ? (PRInt32)d : -(PRInt32)d));
00376 
00377     mStart = mStart2;
00378     mStart2 = 0;
00379   }
00380 #endif
00381 
00382   PRIntervalTime timeout = mTimeout;
00383   if (mType == TYPE_REPEATING_PRECISE) {
00384     // Precise repeating timers advance mTimeout by mDelay without fail before
00385     // calling Fire().
00386     timeout -= PR_MillisecondsToInterval(mDelay);
00387   }
00388   gThread->UpdateFilter(mDelay, timeout, now);
00389 
00390   mFiring = PR_TRUE;
00391 
00392   switch (mCallbackType) {
00393     case CALLBACK_TYPE_FUNC:
00394       mCallback.c(this, mClosure);
00395       break;
00396     case CALLBACK_TYPE_INTERFACE:
00397       mCallback.i->Notify(this);
00398       break;
00399     case CALLBACK_TYPE_OBSERVER:
00400       mCallback.o->Observe(NS_STATIC_CAST(nsITimer*,this),
00401                            NS_TIMER_CALLBACK_TOPIC,
00402                            nsnull);
00403       break;
00404     default:;
00405   }
00406 
00407   mFiring = PR_FALSE;
00408 
00409 #ifdef DEBUG_TIMERS
00410   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
00411     PR_LOG(gTimerLog, PR_LOG_DEBUG,
00412            ("[this=%p] Took %dms to fire timer callback\n",
00413             this, PR_IntervalToMilliseconds(PR_IntervalNow() - now)));
00414   }
00415 #endif
00416 
00417   if (mType == TYPE_REPEATING_SLACK) {
00418     SetDelayInternal(mDelay); // force mTimeout to be recomputed.
00419     if (gThread)
00420       gThread->AddTimer(this);
00421   }
00422 }
00423 
00424 
00425 struct TimerEventType : public PLEvent {
00426   PRInt32        mGeneration;
00427 #ifdef DEBUG_TIMERS
00428   PRIntervalTime mInitTime;
00429 #endif
00430 };
00431 
00432 
00433 void* handleTimerEvent(TimerEventType* event)
00434 {
00435   nsTimerImpl* timer = NS_STATIC_CAST(nsTimerImpl*, event->owner);
00436   if (event->mGeneration != timer->GetGeneration())
00437     return nsnull;
00438 
00439 #ifdef DEBUG_TIMERS
00440   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
00441     PRIntervalTime now = PR_IntervalNow();
00442     PR_LOG(gTimerLog, PR_LOG_DEBUG,
00443            ("[this=%p] time between PostTimerEvent() and Fire(): %dms\n",
00444             event->owner, PR_IntervalToMilliseconds(now - event->mInitTime)));
00445   }
00446 #endif
00447 
00448   if (gFireOnIdle) {
00449     PRBool idle = PR_FALSE;
00450     timer->GetIdle(&idle);
00451     if (idle) {
00452       NS_ASSERTION(gManager, "Global Thread Manager is null!");
00453       if (gManager)
00454         gManager->AddIdleTimer(timer);
00455       return nsnull;
00456     }
00457   }
00458 
00459   timer->Fire();
00460 
00461   return nsnull;
00462 }
00463 
00464 void destroyTimerEvent(TimerEventType* event)
00465 {
00466   nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl*, event->owner);
00467   NS_RELEASE(timer);
00468   PR_DELETE(event);
00469 }
00470 
00471 
00472 void nsTimerImpl::PostTimerEvent()
00473 {
00474   // XXX we may want to reuse the PLEvent in the case of repeating timers.
00475   TimerEventType* event;
00476 
00477   // construct
00478   event = PR_NEW(TimerEventType);
00479   if (!event)
00480     return;
00481 
00482   // initialize
00483   PL_InitEvent((PLEvent*)event, this,
00484                (PLHandleEventProc)handleTimerEvent,
00485                (PLDestroyEventProc)destroyTimerEvent);
00486 
00487   // Since TimerThread addref'd 'this' for us, we don't need to addref here.
00488   // We will release in destroyMyEvent.  We do need to copy the generation
00489   // number from this timer into the event, so we can avoid firing a timer
00490   // that was re-initialized after being canceled.
00491   event->mGeneration = mGeneration;
00492 
00493 #ifdef DEBUG_TIMERS
00494   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
00495     event->mInitTime = PR_IntervalNow();
00496   }
00497 #endif
00498 
00499   // If this is a repeating precise timer, we need to calculate the time for
00500   // the next timer to fire before we make the callback.
00501   if (mType == TYPE_REPEATING_PRECISE) {
00502     SetDelayInternal(mDelay);
00503     if (gThread)
00504       gThread->AddTimer(this);
00505   }
00506 
00507   PRThread *thread;
00508   nsresult rv = mCallingThread->GetPRThread(&thread);
00509   if (NS_FAILED(rv)) {
00510     NS_WARNING("Dropping timer event because thread is dead");
00511     return;
00512   }
00513 
00514   nsCOMPtr<nsIEventQueue> queue;
00515   if (gThread)
00516     gThread->mEventQueueService->GetThreadEventQueue(thread, getter_AddRefs(queue));
00517   if (queue)
00518     queue->PostEvent(event);
00519 }
00520 
00521 void nsTimerImpl::SetDelayInternal(PRUint32 aDelay)
00522 {
00523   PRIntervalTime delayInterval = PR_MillisecondsToInterval(aDelay);
00524   if (delayInterval > DELAY_INTERVAL_MAX) {
00525     delayInterval = DELAY_INTERVAL_MAX;
00526     aDelay = PR_IntervalToMilliseconds(delayInterval);
00527   }
00528 
00529   mDelay = aDelay;
00530 
00531   PRIntervalTime now = PR_IntervalNow();
00532   if (mTimeout == 0 || mType != TYPE_REPEATING_PRECISE)
00533     mTimeout = now;
00534 
00535   mTimeout += delayInterval;
00536 
00537 #ifdef DEBUG_TIMERS
00538   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
00539     if (mStart == 0)
00540       mStart = now;
00541     else
00542       mStart2 = now;
00543   }
00544 #endif
00545 }
00546 
00551 NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimerManager, nsITimerManager)
00552 
00553 nsTimerManager::nsTimerManager()
00554 {
00555   mLock = PR_NewLock();
00556   gManager = this;
00557 }
00558 
00559 nsTimerManager::~nsTimerManager()
00560 {
00561   gManager = nsnull;
00562   PR_DestroyLock(mLock);
00563 
00564   nsTimerImpl *theTimer;
00565   PRInt32 count = mIdleTimers.Count();
00566 
00567   for (PRInt32 i = 0; i < count; i++) {
00568     theTimer = NS_STATIC_CAST(nsTimerImpl*, mIdleTimers[i]);
00569     NS_IF_RELEASE(theTimer);
00570   }
00571 }
00572 
00573 NS_IMETHODIMP nsTimerManager::SetUseIdleTimers(PRBool aUseIdleTimers)
00574 {
00575   if (aUseIdleTimers == PR_FALSE && gFireOnIdle == PR_TRUE)
00576     return NS_ERROR_FAILURE;
00577 
00578   gFireOnIdle = aUseIdleTimers;
00579 
00580   return NS_OK;
00581 }
00582 
00583 NS_IMETHODIMP nsTimerManager::GetUseIdleTimers(PRBool *aUseIdleTimers)
00584 {
00585   *aUseIdleTimers = gFireOnIdle;
00586   return NS_OK;
00587 }
00588 
00589 NS_IMETHODIMP nsTimerManager::HasIdleTimers(PRBool *aHasTimers)
00590 {
00591   nsAutoLock lock (mLock);
00592   PRUint32 count = mIdleTimers.Count();
00593   *aHasTimers = (count != 0);
00594   return NS_OK;
00595 }
00596 
00597 nsresult nsTimerManager::AddIdleTimer(nsITimer* timer)
00598 {
00599   if (!timer)
00600     return NS_ERROR_FAILURE;
00601   nsAutoLock lock(mLock);
00602   mIdleTimers.AppendElement(timer);
00603   NS_ADDREF(timer);
00604   return NS_OK;
00605 }
00606 
00607 NS_IMETHODIMP nsTimerManager::FireNextIdleTimer()
00608 {
00609   if (!gFireOnIdle || !nsIThread::IsMainThread()) {
00610     return NS_OK;
00611   }
00612 
00613   nsTimerImpl *theTimer = nsnull;
00614 
00615   {
00616     nsAutoLock lock (mLock);
00617     PRUint32 count = mIdleTimers.Count();
00618     
00619     if (count == 0) 
00620       return NS_OK;
00621     
00622     theTimer = NS_STATIC_CAST(nsTimerImpl*, mIdleTimers[0]);
00623     mIdleTimers.RemoveElement(theTimer);
00624   }
00625 
00626   theTimer->Fire();
00627 
00628   NS_RELEASE(theTimer);
00629 
00630   return NS_OK;
00631 }
00632 
00633 
00634 // NOT FOR PUBLIC CONSUMPTION!
00635 nsresult
00636 NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
00637             PRUint32 aDelay, PRUint32 aType)
00638 {
00639     nsTimerImpl* timer = new nsTimerImpl();
00640     if (timer == nsnull)
00641         return NS_ERROR_OUT_OF_MEMORY;
00642     NS_ADDREF(timer);
00643 
00644     nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure, 
00645                                               aDelay, aType);
00646     if (NS_FAILED(rv)) {
00647         NS_RELEASE(timer);
00648         return rv;
00649     }
00650 
00651     *aResult = timer;
00652     return NS_OK;
00653 }