LCOV - app.info - xpcom/threads/nsTimerImpl.cpp

LCOV - code coverage report
| Current view: | directory - xpcom/threads - nsTimerImpl.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 275 | 197 | 71.6 %
| Date: | 2012-04-21 | Functions: | 40 | 32 | 80.0 %

           1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
           2                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is mozilla.org code.
          18                 :  *
          19                 :  * The Initial Developer of the Original Code is
          20                 :  * Netscape Communications Corporation.
          21                 :  * Portions created by the Initial Developer are Copyright (C) 2001
          22                 :  * the Initial Developer. All Rights Reserved.
          23                 :  *
          24                 :  * Contributor(s):
          25                 :  *   Stuart Parmenter <pavlov@netscape.com>
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : #include "nsTimerImpl.h"
          42                 : #include "TimerThread.h"
          43                 : #include "nsAutoPtr.h"
          44                 : #include "nsThreadManager.h"
          45                 : #include "nsThreadUtils.h"
          46                 : #include "prmem.h"
          47                 : #include "sampler.h"
          48                 : #include NEW_H
          49                 : #include "nsFixedSizeAllocator.h"
          50                 :
          51                 : using mozilla::TimeDuration;
          52                 : using mozilla::TimeStamp;
          53                 :
          54                 : static PRInt32          gGenerator = 0;
          55                 : static TimerThread*     gThread = nsnull;
          56                 :
          57                 : #ifdef DEBUG_TIMERS
          58                 : #include <math.h>
          59                 :
          60                 : double nsTimerImpl::sDeltaSumSquared = 0;
          61                 : double nsTimerImpl::sDeltaSum = 0;
          62                 : double nsTimerImpl::sDeltaNum = 0;
          63                 :
          64                 : static void
          65               0 : myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
          66                 :                    double *meanResult, double *stdDevResult)
          67                 : {
          68               0 :   double mean = 0.0, var = 0.0, stdDev = 0.0;
          69               0 :   if (n > 0.0 && sumOfValues >= 0) {
          70               0 :     mean = sumOfValues / n;
          71               0 :     double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
          72               0 :     if (temp < 0.0 || n <= 1)
          73               0 :       var = 0.0;
          74                 :     else
          75               0 :       var = temp / (n * (n - 1));
          76                 :     // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
          77               0 :     stdDev = var != 0.0 ? sqrt(var) : 0.0;
          78                 :   }
          79               0 :   *meanResult = mean;
          80               0 :   *stdDevResult = stdDev;
          81               0 : }
          82                 : #endif
          83                 :
          84                 : namespace {
          85                 :
          86                 : // TimerEventAllocator is a fixed size allocator class which is used in order
          87                 : // to avoid the default allocator lock contention when firing timer events.
          88                 : // It is a thread-safe wrapper around nsFixedSizeAllocator.  The thread-safety
          89                 : // is required because nsTimerEvent objects are allocated on the timer thread,
          90                 : // and freed on the main thread.  Since this is a TimerEventAllocator specific
          91                 : // lock, the lock contention issue is only limited to the allocation and
          92                 : // deallocation of nsTimerEvent objects.
          93            1365 : class TimerEventAllocator : public nsFixedSizeAllocator {
          94                 : public:
          95            1365 :     TimerEventAllocator() :
          96            1365 :       mMonitor("TimerEventAllocator")
          97                 :   {
          98            1365 :   }
          99                 :
         100            5308 :   void* Alloc(size_t aSize)
         101                 :   {
         102           10616 :     mozilla::MonitorAutoLock lock(mMonitor);
         103            5308 :     return nsFixedSizeAllocator::Alloc(aSize);
         104                 :   }
         105            5308 :   void Free(void* aPtr, size_t aSize)
         106                 :   {
         107           10616 :     mozilla::MonitorAutoLock lock(mMonitor);
         108            5308 :     nsFixedSizeAllocator::Free(aPtr, aSize);
         109            5308 :   }
         110                 :
         111                 : private:
         112                 :   mozilla::Monitor mMonitor;
         113                 : };
         114                 :
         115                 : }
         116                 :
         117                 : class nsTimerEvent : public nsRunnable {
         118                 : public:
         119                 :   NS_IMETHOD Run();
         120                 :
         121            5308 :   nsTimerEvent(nsTimerImpl *timer, PRInt32 generation)
         122            5308 :     : mTimer(timer), mGeneration(generation) {
         123                 :     // timer is already addref'd for us
         124            5308 :     MOZ_COUNT_CTOR(nsTimerEvent);
         125            5308 :   }
         126                 :
         127                 : #ifdef DEBUG_TIMERS
         128                 :   TimeStamp mInitTime;
         129                 : #endif
         130                 :
         131                 :   static void Init();
         132                 :   static void Shutdown();
         133                 :
         134            5308 :   static void* operator new(size_t size) CPP_THROW_NEW {
         135            5308 :     return sAllocator->Alloc(size);
         136                 :   }
         137            5308 :   void operator delete(void* p) {
         138            5308 :     sAllocator->Free(p, sizeof(nsTimerEvent));
         139            5308 :   }
         140                 :
         141                 : private:
         142           15924 :   ~nsTimerEvent() {
         143                 : #ifdef DEBUG
         144            5308 :     if (mTimer)
         145               0 :       NS_WARNING("leaking reference to nsTimerImpl");
         146                 : #endif
         147            5308 :     MOZ_COUNT_DTOR(nsTimerEvent);
         148           10616 :   }
         149                 :
         150                 :   nsTimerImpl *mTimer;
         151                 :   PRInt32      mGeneration;
         152                 :
         153                 :   static TimerEventAllocator* sAllocator;
         154                 : };
         155                 :
         156                 : TimerEventAllocator* nsTimerEvent::sAllocator = nsnull;
         157                 :
         158           58039 : NS_IMPL_THREADSAFE_QUERY_INTERFACE1(nsTimerImpl, nsITimer)
         159           76438 : NS_IMPL_THREADSAFE_ADDREF(nsTimerImpl)
         160                 :
         161           76435 : NS_IMETHODIMP_(nsrefcnt) nsTimerImpl::Release(void)
         162                 : {
         163                 :   nsrefcnt count;
         164                 :
         165           76435 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
         166           76435 :   count = NS_AtomicDecrementRefcnt(mRefCnt);
         167           76435 :   NS_LOG_RELEASE(this, count, "nsTimerImpl");
         168           76435 :   if (count == 0) {
         169           12319 :     mRefCnt = 1; /* stabilize */
         170                 :
         171                 :     /* enable this to find non-threadsafe destructors: */
         172                 :     /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
         173           12319 :     delete this;
         174           12319 :     return 0;
         175                 :   }
         176                 :
         177                 :   // If only one reference remains, and mArmed is set, then the ref must be
         178                 :   // from the TimerThread::mTimers array, so we Cancel this timer to remove
         179                 :   // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
         180                 :   //
         181                 :   // We use an inlined version of nsTimerImpl::Cancel here to check for the
         182                 :   // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
         183                 :   // timer is not found in the mTimers array -- i.e., when the timer was not
         184                 :   // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
         185                 :   // being true here.  That can happen if the armed timer is being fired by
         186                 :   // TimerThread::Run as we race and test mArmed just before it is cleared by
         187                 :   // the timer thread.  If the RemoveTimer call below doesn't find this timer
         188                 :   // in the mTimers array, then the last ref to this timer is held manually
         189                 :   // and temporarily by the TimerThread, so we should fall through to the
         190                 :   // final return and return 1, not 0.
         191                 :   //
         192                 :   // The original version of this thread-based timer code kept weak refs from
         193                 :   // TimerThread::mTimers, removing this timer's weak ref in the destructor,
         194                 :   // but that leads to double-destructions in the race described above, and
         195                 :   // adding mArmed doesn't help, because destructors can't be deferred, once
         196                 :   // begun.  But by combining reference-counting and a specialized Release
         197                 :   // method with "is this timer still in the mTimers array once we acquire
         198                 :   // the TimerThread's lock" testing, we defer destruction until we're sure
         199                 :   // that only one thread has its hot little hands on this timer.
         200                 :   //
         201                 :   // Note that both approaches preclude a timer creator, and everyone else
         202                 :   // except the TimerThread who might have a strong ref, from dropping all
         203                 :   // their strong refs without implicitly canceling the timer.  Timers need
         204                 :   // non-mTimers-element strong refs to stay alive.
         205                 :
         206           64116 :   if (count == 1 && mArmed) {
         207              84 :     mCanceled = true;
         208                 :
         209              84 :     NS_ASSERTION(gThread, "An armed timer exists after the thread timer stopped.");
         210              84 :     if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
         211              84 :       return 0;
         212                 :   }
         213                 :
         214           64032 :   return count;
         215                 : }
         216                 :
         217           12322 : nsTimerImpl::nsTimerImpl() :
         218                 :   mClosure(nsnull),
         219                 :   mCallbackType(CALLBACK_TYPE_UNKNOWN),
         220                 :   mFiring(false),
         221                 :   mArmed(false),
         222                 :   mCanceled(false),
         223                 :   mGeneration(0),
         224           12322 :   mDelay(0)
         225                 : {
         226                 :   // XXXbsmedberg: shouldn't this be in Init()?
         227           12322 :   mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
         228                 :
         229           12322 :   mCallback.c = nsnull;
         230           12322 : }
         231                 :
         232           24638 : nsTimerImpl::~nsTimerImpl()
         233                 : {
         234           12319 :   ReleaseCallback();
         235           12319 : }
         236                 :
         237                 : //static
         238                 : nsresult
         239            1365 : nsTimerImpl::Startup()
         240                 : {
         241                 :   nsresult rv;
         242                 :
         243            1365 :   nsTimerEvent::Init();
         244                 :
         245            1365 :   gThread = new TimerThread();
         246            1365 :   if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
         247                 :
         248            1365 :   NS_ADDREF(gThread);
         249            1365 :   rv = gThread->InitLocks();
         250                 :
         251            1365 :   if (NS_FAILED(rv)) {
         252               0 :     NS_RELEASE(gThread);
         253                 :   }
         254                 :
         255            1365 :   return rv;
         256                 : }
         257                 :
         258            1365 : void nsTimerImpl::Shutdown()
         259                 : {
         260                 : #ifdef DEBUG_TIMERS
         261            1365 :   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
         262               0 :     double mean = 0, stddev = 0;
         263               0 :     myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
         264                 :
         265               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
         266               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
         267                 :   }
         268                 : #endif
         269                 :
         270            1365 :   if (!gThread)
         271               0 :     return;
         272                 :
         273            1365 :   gThread->Shutdown();
         274            1365 :   NS_RELEASE(gThread);
         275                 :
         276            1365 :   nsTimerEvent::Shutdown();
         277                 : }
         278                 :
         279                 :
         280           21337 : nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay)
         281                 : {
         282                 :   nsresult rv;
         283                 :
         284           21337 :   NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED);
         285                 :
         286           21337 :   rv = gThread->Init();
         287           21337 :   NS_ENSURE_SUCCESS(rv, rv);
         288                 :
         289                 :   /**
         290                 :    * In case of re-Init, both with and without a preceding Cancel, clear the
         291                 :    * mCanceled flag and assign a new mGeneration.  But first, remove any armed
         292                 :    * timer from the timer thread's list.
         293                 :    *
         294                 :    * If we are racing with the timer thread to remove this timer and we lose,
         295                 :    * the RemoveTimer call made here will fail to find this timer in the timer
         296                 :    * thread's list, and will return false harmlessly.  We test mArmed here to
         297                 :    * avoid the small overhead in RemoveTimer of locking the timer thread and
         298                 :    * checking its list for this timer.  It's safe to test mArmed even though
         299                 :    * it might be cleared on another thread in the next cycle (or even already
         300                 :    * be cleared by another CPU whose store hasn't reached our CPU's cache),
         301                 :    * because RemoveTimer is idempotent.
         302                 :    */
         303           21337 :   if (mArmed)
         304               0 :     gThread->RemoveTimer(this);
         305           21337 :   mCanceled = false;
         306           21337 :   mTimeout = TimeStamp();
         307           21337 :   mGeneration = PR_ATOMIC_INCREMENT(&gGenerator);
         308                 :
         309           21337 :   mType = (PRUint8)aType;
         310           21337 :   SetDelayInternal(aDelay);
         311                 :
         312           21337 :   return gThread->AddTimer(this);
         313                 : }
         314                 :
         315            8655 : NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
         316                 :                                                 void *aClosure,
         317                 :                                                 PRUint32 aDelay,
         318                 :                                                 PRUint32 aType)
         319                 : {
         320            8655 :   NS_ENSURE_ARG_POINTER(aFunc);
         321                 :
         322            8655 :   ReleaseCallback();
         323            8655 :   mCallbackType = CALLBACK_TYPE_FUNC;
         324            8655 :   mCallback.c = aFunc;
         325            8655 :   mClosure = aClosure;
         326                 :
         327            8655 :   return InitCommon(aType, aDelay);
         328                 : }
         329                 :
         330            9862 : NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
         331                 :                                             PRUint32 aDelay,
         332                 :                                             PRUint32 aType)
         333                 : {
         334            9862 :   NS_ENSURE_ARG_POINTER(aCallback);
         335                 :
         336            9862 :   ReleaseCallback();
         337            9862 :   mCallbackType = CALLBACK_TYPE_INTERFACE;
         338            9862 :   mCallback.i = aCallback;
         339            9862 :   NS_ADDREF(mCallback.i);
         340                 :
         341            9862 :   return InitCommon(aType, aDelay);
         342                 : }
         343                 :
         344            2820 : NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
         345                 :                                 PRUint32 aDelay,
         346                 :                                 PRUint32 aType)
         347                 : {
         348            2820 :   NS_ENSURE_ARG_POINTER(aObserver);
         349                 :
         350            2820 :   ReleaseCallback();
         351            2820 :   mCallbackType = CALLBACK_TYPE_OBSERVER;
         352            2820 :   mCallback.o = aObserver;
         353            2820 :   NS_ADDREF(mCallback.o);
         354                 :
         355            2820 :   return InitCommon(aType, aDelay);
         356                 : }
         357                 :
         358           20308 : NS_IMETHODIMP nsTimerImpl::Cancel()
         359                 : {
         360           20308 :   mCanceled = true;
         361                 :
         362           20308 :   if (gThread)
         363           19956 :     gThread->RemoveTimer(this);
         364                 :
         365           20308 :   ReleaseCallback();
         366                 :
         367           20308 :   return NS_OK;
         368                 : }
         369                 :
         370            3657 : NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay)
         371                 : {
         372            3657 :   if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) {
         373                 :     // This may happen if someone tries to re-use a one-shot timer
         374                 :     // by re-setting delay instead of reinitializing the timer.
         375                 :     NS_ERROR("nsITimer->SetDelay() called when the "
         376               0 :              "one-shot timer is not set up.");
         377               0 :     return NS_ERROR_NOT_INITIALIZED;
         378                 :   }
         379                 :
         380                 :   // If we're already repeating precisely, update mTimeout now so that the
         381                 :   // new delay takes effect in the future.
         382            3657 :   if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
         383               0 :     mTimeout = TimeStamp::Now();
         384                 :
         385            3657 :   SetDelayInternal(aDelay);
         386                 :
         387            3657 :   if (!mFiring && gThread)
         388            3651 :     gThread->TimerDelayChanged(this);
         389                 :
         390            3657 :   return NS_OK;
         391                 : }
         392                 :
         393              39 : NS_IMETHODIMP nsTimerImpl::GetDelay(PRUint32* aDelay)
         394                 : {
         395              39 :   *aDelay = mDelay;
         396              39 :   return NS_OK;
         397                 : }
         398                 :
         399               0 : NS_IMETHODIMP nsTimerImpl::SetType(PRUint32 aType)
         400                 : {
         401               0 :   mType = (PRUint8)aType;
         402                 :   // XXX if this is called, we should change the actual type.. this could effect
         403                 :   // repeating timers.  we need to ensure in Fire() that if mType has changed
         404                 :   // during the callback that we don't end up with the timer in the queue twice.
         405               0 :   return NS_OK;
         406                 : }
         407                 :
         408               0 : NS_IMETHODIMP nsTimerImpl::GetType(PRUint32* aType)
         409                 : {
         410               0 :   *aType = mType;
         411               0 :   return NS_OK;
         412                 : }
         413                 :
         414                 :
         415               0 : NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
         416                 : {
         417               0 :   *aClosure = mClosure;
         418               0 :   return NS_OK;
         419                 : }
         420                 :
         421                 :
         422               0 : NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
         423                 : {
         424               0 :   if (mCallbackType == CALLBACK_TYPE_INTERFACE)
         425               0 :     NS_IF_ADDREF(*aCallback = mCallback.i);
         426               0 :   else if (mTimerCallbackWhileFiring)
         427               0 :     NS_ADDREF(*aCallback = mTimerCallbackWhileFiring);
         428                 :   else
         429               0 :     *aCallback = nsnull;
         430                 :
         431               0 :   return NS_OK;
         432                 : }
         433                 :
         434                 :
         435               0 : NS_IMETHODIMP nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
         436                 : {
         437               0 :   NS_IF_ADDREF(*aTarget = mEventTarget);
         438               0 :   return NS_OK;
         439                 : }
         440                 :
         441                 :
         442             320 : NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
         443                 : {
         444             320 :   NS_ENSURE_TRUE(mCallbackType == CALLBACK_TYPE_UNKNOWN,
         445                 :                  NS_ERROR_ALREADY_INITIALIZED);
         446                 :
         447             320 :   if (aTarget)
         448             320 :     mEventTarget = aTarget;
         449                 :   else
         450               0 :     mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
         451             320 :   return NS_OK;
         452                 : }
         453                 :
         454                 :
         455            5308 : void nsTimerImpl::Fire()
         456                 : {
         457            5308 :   if (mCanceled)
         458             118 :     return;
         459                 :
         460           10380 :   SAMPLE_LABEL("Timer", "Fire");
         461                 :
         462            5190 :   TimeStamp now = TimeStamp::Now();
         463                 : #ifdef DEBUG_TIMERS
         464            5190 :   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
         465               0 :     TimeDuration   a = now - mStart; // actual delay in intervals
         466               0 :     TimeDuration   b = TimeDuration::FromMilliseconds(mDelay); // expected delay in intervals
         467               0 :     TimeDuration   delta = (a > b) ? a - b : b - a;
         468               0 :     PRUint32       d = delta.ToMilliseconds(); // delta in ms
         469               0 :     sDeltaSum += d;
         470               0 :     sDeltaSumSquared += double(d) * double(d);
         471               0 :     sDeltaNum++;
         472                 :
         473               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] expected delay time %4ums\n", this, mDelay));
         474               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] actual delay time   %fms\n", this, a.ToMilliseconds()));
         475               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] (mType is %d)       -------\n", this, mType));
         476               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p]     delta           %4dms\n", this, (a > b) ? (PRInt32)d : -(PRInt32)d));
         477                 :
         478               0 :     mStart = mStart2;
         479               0 :     mStart2 = TimeStamp();
         480                 :   }
         481                 : #endif
         482                 :
         483            5190 :   TimeStamp timeout = mTimeout;
         484            5190 :   if (IsRepeatingPrecisely()) {
         485                 :     // Precise repeating timers advance mTimeout by mDelay without fail before
         486                 :     // calling Fire().
         487               1 :     timeout -= TimeDuration::FromMilliseconds(mDelay);
         488                 :   }
         489            5190 :   if (gThread)
         490            5190 :     gThread->UpdateFilter(mDelay, timeout, now);
         491                 :
         492            5190 :   if (mCallbackType == CALLBACK_TYPE_INTERFACE)
         493            2471 :     mTimerCallbackWhileFiring = mCallback.i;
         494            5190 :   mFiring = true;
         495                 :
         496                 :   // Handle callbacks that re-init the timer, but avoid leaking.
         497                 :   // See bug 330128.
         498            5190 :   CallbackUnion callback = mCallback;
         499            5190 :   PRUintn callbackType = mCallbackType;
         500            5190 :   if (callbackType == CALLBACK_TYPE_INTERFACE)
         501            2471 :     NS_ADDREF(callback.i);
         502            2719 :   else if (callbackType == CALLBACK_TYPE_OBSERVER)
         503              18 :     NS_ADDREF(callback.o);
         504            5190 :   ReleaseCallback();
         505                 :
         506            5190 :   switch (callbackType) {
         507                 :     case CALLBACK_TYPE_FUNC:
         508            2701 :       callback.c(this, mClosure);
         509            2701 :       break;
         510                 :     case CALLBACK_TYPE_INTERFACE:
         511            2471 :       callback.i->Notify(this);
         512            2471 :       break;
         513                 :     case CALLBACK_TYPE_OBSERVER:
         514                 :       callback.o->Observe(static_cast<nsITimer*>(this),
         515                 :                           NS_TIMER_CALLBACK_TOPIC,
         516              18 :                           nsnull);
         517              18 :       break;
         518                 :     default:;
         519                 :   }
         520                 :
         521                 :   // If the callback didn't re-init the timer, and it's not a one-shot timer,
         522                 :   // restore the callback state.
         523            7828 :   if (mCallbackType == CALLBACK_TYPE_UNKNOWN &&
         524            2638 :       mType != TYPE_ONE_SHOT && !mCanceled) {
         525            2555 :     mCallback = callback;
         526            2555 :     mCallbackType = callbackType;
         527                 :   } else {
         528                 :     // The timer was a one-shot, or the callback was reinitialized.
         529            2635 :     if (callbackType == CALLBACK_TYPE_INTERFACE)
         530            2462 :       NS_RELEASE(callback.i);
         531             173 :     else if (callbackType == CALLBACK_TYPE_OBSERVER)
         532              17 :       NS_RELEASE(callback.o);
         533                 :   }
         534                 :
         535            5190 :   mFiring = false;
         536            5190 :   mTimerCallbackWhileFiring = nsnull;
         537                 :
         538                 : #ifdef DEBUG_TIMERS
         539            5190 :   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
         540               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG,
         541                 :            ("[this=%p] Took %fms to fire timer callback\n",
         542                 :             this, (TimeStamp::Now() - now).ToMilliseconds()));
         543                 :   }
         544                 : #endif
         545                 :
         546                 :   // Reschedule repeating timers, except REPEATING_PRECISE which already did
         547                 :   // that in PostTimerEvent, but make sure that we aren't armed already (which
         548                 :   // can happen if the callback reinitialized the timer).
         549            5190 :   if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
         550            2637 :     if (mType == TYPE_REPEATING_SLACK)
         551            2637 :       SetDelayInternal(mDelay); // force mTimeout to be recomputed.  For
         552                 :                                 // REPEATING_PRECISE_CAN_SKIP timers this has
         553                 :                                 // already happened.
         554            2637 :     if (gThread)
         555            2637 :       gThread->AddTimer(this);
         556                 :   }
         557                 : }
         558                 :
         559            1365 : void nsTimerEvent::Init()
         560                 : {
         561            1365 :   sAllocator = new TimerEventAllocator();
         562                 :   static const size_t kBucketSizes[] = {sizeof(nsTimerEvent)};
         563            1365 :   static const PRInt32 kNumBuckets = mozilla::ArrayLength(kBucketSizes);
         564                 :   static const PRInt32 kInitialPoolSize = 1024 * NS_SIZE_IN_HEAP(sizeof(nsTimerEvent));
         565            1365 :   sAllocator->Init("TimerEventPool", kBucketSizes, kNumBuckets, kInitialPoolSize);
         566            1365 : }
         567                 :
         568            1365 : void nsTimerEvent::Shutdown()
         569                 : {
         570            1365 :   delete sAllocator;
         571            1365 :   sAllocator = nsnull;
         572            1365 : }
         573                 :
         574            5308 : NS_IMETHODIMP nsTimerEvent::Run()
         575                 : {
         576           10616 :   nsRefPtr<nsTimerImpl> timer;
         577            5308 :   timer.swap(mTimer);
         578                 :
         579            5308 :   if (mGeneration != timer->GetGeneration())
         580               0 :     return NS_OK;
         581                 :
         582                 : #ifdef DEBUG_TIMERS
         583            5308 :   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
         584               0 :     TimeStamp now = TimeStamp::Now();
         585               0 :     PR_LOG(gTimerLog, PR_LOG_DEBUG,
         586                 :            ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
         587                 :             this, (now - mInitTime).ToMilliseconds()));
         588                 :   }
         589                 : #endif
         590                 :
         591            5308 :   timer->Fire();
         592                 :
         593            5308 :   return NS_OK;
         594                 : }
         595                 :
         596            5308 : nsresult nsTimerImpl::PostTimerEvent()
         597                 : {
         598                 :   // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
         599                 :
         600                 :   // Since TimerThread addref'd 'this' for us, we don't need to addref here.
         601                 :   // We will release in destroyMyEvent.  We need to copy the generation number
         602                 :   // from this timer into the event, so we can avoid firing a timer that was
         603                 :   // re-initialized after being canceled.
         604                 :
         605           10616 :   nsRefPtr<nsTimerEvent> event = new nsTimerEvent(this, mGeneration);
         606            5308 :   if (!event)
         607               0 :     return NS_ERROR_OUT_OF_MEMORY;
         608                 :
         609                 : #ifdef DEBUG_TIMERS
         610            5308 :   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
         611               0 :     event->mInitTime = TimeStamp::Now();
         612                 :   }
         613                 : #endif
         614                 :
         615                 :   // If this is a repeating precise timer, we need to calculate the time for
         616                 :   // the next timer to fire before we make the callback.
         617            5308 :   if (IsRepeatingPrecisely()) {
         618               1 :     SetDelayInternal(mDelay);
         619                 :
         620                 :     // But only re-arm REPEATING_PRECISE timers.
         621               1 :     if (gThread && mType == TYPE_REPEATING_PRECISE) {
         622               1 :       nsresult rv = gThread->AddTimer(this);
         623               1 :       if (NS_FAILED(rv))
         624               0 :         return rv;
         625                 :     }
         626                 :   }
         627                 :
         628            5308 :   nsresult rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
         629            5308 :   if (NS_FAILED(rv) && gThread)
         630               0 :     gThread->RemoveTimer(this);
         631            5308 :   return rv;
         632                 : }
         633                 :
         634           27632 : void nsTimerImpl::SetDelayInternal(PRUint32 aDelay)
         635                 : {
         636           27632 :   TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
         637                 :
         638           27632 :   mDelay = aDelay;
         639                 :
         640           27632 :   TimeStamp now = TimeStamp::Now();
         641           27632 :   if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
         642           27631 :     mTimeout = now;
         643                 :
         644           27632 :   mTimeout += delayInterval;
         645                 :
         646                 : #ifdef DEBUG_TIMERS
         647           27632 :   if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
         648               0 :     if (mStart.IsNull())
         649               0 :       mStart = now;
         650                 :     else
         651               0 :       mStart2 = now;
         652                 :   }
         653                 : #endif
         654           27632 : }
         655                 :
         656                 : // NOT FOR PUBLIC CONSUMPTION!
         657                 : nsresult
         658               0 : NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
         659                 :             PRUint32 aDelay, PRUint32 aType)
         660                 : {
         661               0 :     nsTimerImpl* timer = new nsTimerImpl();
         662               0 :     if (timer == nsnull)
         663               0 :         return NS_ERROR_OUT_OF_MEMORY;
         664               0 :     NS_ADDREF(timer);
         665                 :
         666                 :     nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure,
         667               0 :                                               aDelay, aType);
         668               0 :     if (NS_FAILED(rv)) {
         669               0 :         NS_RELEASE(timer);
         670               0 :         return rv;
         671                 :     }
         672                 :
         673               0 :     *aResult = timer;
         674               0 :     return NS_OK;
         675            4188 : }

Generated by: LCOV version 1.7