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-06-02 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            1419 : class TimerEventAllocator : public nsFixedSizeAllocator {
      94                 : public:
      95            1419 :     TimerEventAllocator() :
      96            1419 :       mMonitor("TimerEventAllocator")
      97                 :   {
      98            1419 :   }
      99                 : 
     100            4298 :   void* Alloc(size_t aSize)
     101                 :   {
     102            8596 :     mozilla::MonitorAutoLock lock(mMonitor);
     103            4298 :     return nsFixedSizeAllocator::Alloc(aSize);
     104                 :   }
     105            4298 :   void Free(void* aPtr, size_t aSize)
     106                 :   {
     107            8596 :     mozilla::MonitorAutoLock lock(mMonitor);
     108            4298 :     nsFixedSizeAllocator::Free(aPtr, aSize);
     109            4298 :   }
     110                 : 
     111                 : private:
     112                 :   mozilla::Monitor mMonitor;
     113                 : };
     114                 : 
     115                 : }
     116                 : 
     117                 : class nsTimerEvent : public nsRunnable {
     118                 : public:
     119                 :   NS_IMETHOD Run();
     120                 : 
     121            4298 :   nsTimerEvent(nsTimerImpl *timer, PRInt32 generation)
     122            4298 :     : mTimer(timer), mGeneration(generation) {
     123                 :     // timer is already addref'd for us
     124            4298 :     MOZ_COUNT_CTOR(nsTimerEvent);
     125            4298 :   }
     126                 : 
     127                 : #ifdef DEBUG_TIMERS
     128                 :   TimeStamp mInitTime;
     129                 : #endif
     130                 : 
     131                 :   static void Init();
     132                 :   static void Shutdown();
     133                 : 
     134            4298 :   static void* operator new(size_t size) CPP_THROW_NEW {
     135            4298 :     return sAllocator->Alloc(size);
     136                 :   }
     137            4298 :   void operator delete(void* p) {
     138            4298 :     sAllocator->Free(p, sizeof(nsTimerEvent));
     139            4298 :   }
     140                 : 
     141                 : private:
     142           12894 :   ~nsTimerEvent() {
     143                 : #ifdef DEBUG
     144            4298 :     if (mTimer)
     145               0 :       NS_WARNING("leaking reference to nsTimerImpl");
     146                 : #endif
     147            4298 :     MOZ_COUNT_DTOR(nsTimerEvent);
     148            8596 :   }
     149                 : 
     150                 :   nsTimerImpl *mTimer;
     151                 :   PRInt32      mGeneration;
     152                 : 
     153                 :   static TimerEventAllocator* sAllocator;
     154                 : };
     155                 : 
     156                 : TimerEventAllocator* nsTimerEvent::sAllocator = nsnull;
     157                 : 
     158           57582 : NS_IMPL_THREADSAFE_QUERY_INTERFACE1(nsTimerImpl, nsITimer)
     159           74346 : NS_IMPL_THREADSAFE_ADDREF(nsTimerImpl)
     160                 : 
     161           74343 : NS_IMETHODIMP_(nsrefcnt) nsTimerImpl::Release(void)
     162                 : {
     163                 :   nsrefcnt count;
     164                 : 
     165           74343 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
     166           74343 :   count = NS_AtomicDecrementRefcnt(mRefCnt);
     167           74343 :   NS_LOG_RELEASE(this, count, "nsTimerImpl");
     168           74343 :   if (count == 0) {
     169           12301 :     mRefCnt = 1; /* stabilize */
     170                 : 
     171                 :     /* enable this to find non-threadsafe destructors: */
     172                 :     /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
     173           12301 :     delete this;
     174           12301 :     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           62042 :   if (count == 1 && mArmed) {
     207              51 :     mCanceled = true;
     208                 : 
     209              51 :     NS_ASSERTION(gThread, "An armed timer exists after the thread timer stopped.");
     210              51 :     if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
     211              51 :       return 0;
     212                 :   }
     213                 : 
     214           61991 :   return count;
     215                 : }
     216                 : 
     217           12304 : nsTimerImpl::nsTimerImpl() :
     218                 :   mClosure(nsnull),
     219                 :   mCallbackType(CALLBACK_TYPE_UNKNOWN),
     220                 :   mFiring(false),
     221                 :   mArmed(false),
     222                 :   mCanceled(false),
     223                 :   mGeneration(0),
     224           12304 :   mDelay(0)
     225                 : {
     226                 :   // XXXbsmedberg: shouldn't this be in Init()?
     227           12304 :   mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
     228                 : 
     229           12304 :   mCallback.c = nsnull;
     230           12304 : }
     231                 : 
     232           24602 : nsTimerImpl::~nsTimerImpl()
     233                 : {
     234           12301 :   ReleaseCallback();
     235           12301 : }
     236                 : 
     237                 : //static
     238                 : nsresult
     239            1419 : nsTimerImpl::Startup()
     240                 : {
     241                 :   nsresult rv;
     242                 : 
     243            1419 :   nsTimerEvent::Init();
     244                 : 
     245            1419 :   gThread = new TimerThread();
     246            1419 :   if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
     247                 : 
     248            1419 :   NS_ADDREF(gThread);
     249            1419 :   rv = gThread->InitLocks();
     250                 : 
     251            1419 :   if (NS_FAILED(rv)) {
     252               0 :     NS_RELEASE(gThread);
     253                 :   }
     254                 : 
     255            1419 :   return rv;
     256                 : }
     257                 : 
     258            1419 : void nsTimerImpl::Shutdown()
     259                 : {
     260                 : #ifdef DEBUG_TIMERS
     261            1419 :   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            1419 :   if (!gThread)
     271               0 :     return;
     272                 : 
     273            1419 :   gThread->Shutdown();
     274            1419 :   NS_RELEASE(gThread);
     275                 : 
     276            1419 :   nsTimerEvent::Shutdown();
     277                 : }
     278                 : 
     279                 : 
     280           21292 : nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay)
     281                 : {
     282                 :   nsresult rv;
     283                 : 
     284           21292 :   NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED);
     285                 : 
     286           21292 :   rv = gThread->Init();
     287           21292 :   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           21292 :   if (mArmed)
     304               0 :     gThread->RemoveTimer(this);
     305           21292 :   mCanceled = false;
     306           21292 :   mTimeout = TimeStamp();
     307           21292 :   mGeneration = PR_ATOMIC_INCREMENT(&gGenerator);
     308                 : 
     309           21292 :   mType = (PRUint8)aType;
     310           21292 :   SetDelayInternal(aDelay);
     311                 : 
     312           21292 :   return gThread->AddTimer(this);
     313                 : }
     314                 : 
     315            8659 : NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
     316                 :                                                 void *aClosure,
     317                 :                                                 PRUint32 aDelay,
     318                 :                                                 PRUint32 aType)
     319                 : {
     320            8659 :   NS_ENSURE_ARG_POINTER(aFunc);
     321                 :   
     322            8659 :   ReleaseCallback();
     323            8659 :   mCallbackType = CALLBACK_TYPE_FUNC;
     324            8659 :   mCallback.c = aFunc;
     325            8659 :   mClosure = aClosure;
     326                 : 
     327            8659 :   return InitCommon(aType, aDelay);
     328                 : }
     329                 : 
     330            9808 : NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
     331                 :                                             PRUint32 aDelay,
     332                 :                                             PRUint32 aType)
     333                 : {
     334            9808 :   NS_ENSURE_ARG_POINTER(aCallback);
     335                 : 
     336            9808 :   ReleaseCallback();
     337            9808 :   mCallbackType = CALLBACK_TYPE_INTERFACE;
     338            9808 :   mCallback.i = aCallback;
     339            9808 :   NS_ADDREF(mCallback.i);
     340                 : 
     341            9808 :   return InitCommon(aType, aDelay);
     342                 : }
     343                 : 
     344            2825 : NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
     345                 :                                 PRUint32 aDelay,
     346                 :                                 PRUint32 aType)
     347                 : {
     348            2825 :   NS_ENSURE_ARG_POINTER(aObserver);
     349                 : 
     350            2825 :   ReleaseCallback();
     351            2825 :   mCallbackType = CALLBACK_TYPE_OBSERVER;
     352            2825 :   mCallback.o = aObserver;
     353            2825 :   NS_ADDREF(mCallback.o);
     354                 : 
     355            2825 :   return InitCommon(aType, aDelay);
     356                 : }
     357                 : 
     358           20237 : NS_IMETHODIMP nsTimerImpl::Cancel()
     359                 : {
     360           20237 :   mCanceled = true;
     361                 : 
     362           20237 :   if (gThread)
     363           19885 :     gThread->RemoveTimer(this);
     364                 : 
     365           20237 :   ReleaseCallback();
     366                 : 
     367           20237 :   return NS_OK;
     368                 : }
     369                 : 
     370            3664 : NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay)
     371                 : {
     372            3664 :   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            3664 :   if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
     383               0 :     mTimeout = TimeStamp::Now();
     384                 : 
     385            3664 :   SetDelayInternal(aDelay);
     386                 : 
     387            3664 :   if (!mFiring && gThread)
     388            3658 :     gThread->TimerDelayChanged(this);
     389                 : 
     390            3664 :   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             321 : NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
     443                 : {
     444             321 :   NS_ENSURE_TRUE(mCallbackType == CALLBACK_TYPE_UNKNOWN,
     445                 :                  NS_ERROR_ALREADY_INITIALIZED);
     446                 : 
     447             321 :   if (aTarget)
     448             321 :     mEventTarget = aTarget;
     449                 :   else
     450               0 :     mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
     451             321 :   return NS_OK;
     452                 : }
     453                 : 
     454                 : 
     455            4298 : void nsTimerImpl::Fire()
     456                 : {
     457            4298 :   if (mCanceled)
     458              55 :     return;
     459                 : 
     460            8486 :   SAMPLE_LABEL("Timer", "Fire");
     461                 : 
     462            4243 :   TimeStamp now = TimeStamp::Now();
     463                 : #ifdef DEBUG_TIMERS
     464            4243 :   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            4243 :   TimeStamp timeout = mTimeout;
     484            4243 :   if (IsRepeatingPrecisely()) {
     485                 :     // Precise repeating timers advance mTimeout by mDelay without fail before
     486                 :     // calling Fire().
     487               1 :     timeout -= TimeDuration::FromMilliseconds(mDelay);
     488                 :   }
     489            4243 :   if (gThread)
     490            4243 :     gThread->UpdateFilter(mDelay, timeout, now);
     491                 : 
     492            4243 :   if (mCallbackType == CALLBACK_TYPE_INTERFACE)
     493            2415 :     mTimerCallbackWhileFiring = mCallback.i;
     494            4243 :   mFiring = true;
     495                 :   
     496                 :   // Handle callbacks that re-init the timer, but avoid leaking.
     497                 :   // See bug 330128.
     498            4243 :   CallbackUnion callback = mCallback;
     499            4243 :   PRUintn callbackType = mCallbackType;
     500            4243 :   if (callbackType == CALLBACK_TYPE_INTERFACE)
     501            2415 :     NS_ADDREF(callback.i);
     502            1828 :   else if (callbackType == CALLBACK_TYPE_OBSERVER)
     503              19 :     NS_ADDREF(callback.o);
     504            4243 :   ReleaseCallback();
     505                 : 
     506            4243 :   switch (callbackType) {
     507                 :     case CALLBACK_TYPE_FUNC:
     508            1809 :       callback.c(this, mClosure);
     509            1809 :       break;
     510                 :     case CALLBACK_TYPE_INTERFACE:
     511            2415 :       callback.i->Notify(this);
     512            2415 :       break;
     513                 :     case CALLBACK_TYPE_OBSERVER:
     514                 :       callback.o->Observe(static_cast<nsITimer*>(this),
     515                 :                           NS_TIMER_CALLBACK_TOPIC,
     516              19 :                           nsnull);
     517              19 :       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            5994 :   if (mCallbackType == CALLBACK_TYPE_UNKNOWN &&
     524            1751 :       mType != TYPE_ONE_SHOT && !mCanceled) {
     525            1701 :     mCallback = callback;
     526            1701 :     mCallbackType = callbackType;
     527                 :   } else {
     528                 :     // The timer was a one-shot, or the callback was reinitialized.
     529            2542 :     if (callbackType == CALLBACK_TYPE_INTERFACE)
     530            2406 :       NS_RELEASE(callback.i);
     531             136 :     else if (callbackType == CALLBACK_TYPE_OBSERVER)
     532              19 :       NS_RELEASE(callback.o);
     533                 :   }
     534                 : 
     535            4243 :   mFiring = false;
     536            4243 :   mTimerCallbackWhileFiring = nsnull;
     537                 : 
     538                 : #ifdef DEBUG_TIMERS
     539            4243 :   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            4243 :   if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
     550            1750 :     if (mType == TYPE_REPEATING_SLACK)
     551            1750 :       SetDelayInternal(mDelay); // force mTimeout to be recomputed.  For
     552                 :                                 // REPEATING_PRECISE_CAN_SKIP timers this has
     553                 :                                 // already happened.
     554            1750 :     if (gThread)
     555            1750 :       gThread->AddTimer(this);
     556                 :   }
     557                 : }
     558                 : 
     559            1419 : void nsTimerEvent::Init()
     560                 : {
     561            1419 :   sAllocator = new TimerEventAllocator();
     562                 :   static const size_t kBucketSizes[] = {sizeof(nsTimerEvent)};
     563            1419 :   static const PRInt32 kNumBuckets = mozilla::ArrayLength(kBucketSizes);
     564                 :   static const PRInt32 kInitialPoolSize = 1024 * NS_SIZE_IN_HEAP(sizeof(nsTimerEvent));
     565            1419 :   sAllocator->Init("TimerEventPool", kBucketSizes, kNumBuckets, kInitialPoolSize);
     566            1419 : }
     567                 : 
     568            1419 : void nsTimerEvent::Shutdown()
     569                 : {
     570            1419 :   delete sAllocator;
     571            1419 :   sAllocator = nsnull;
     572            1419 : }
     573                 : 
     574            4298 : NS_IMETHODIMP nsTimerEvent::Run()
     575                 : {
     576            8596 :   nsRefPtr<nsTimerImpl> timer;
     577            4298 :   timer.swap(mTimer);
     578                 : 
     579            4298 :   if (mGeneration != timer->GetGeneration())
     580               0 :     return NS_OK;
     581                 : 
     582                 : #ifdef DEBUG_TIMERS
     583            4298 :   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            4298 :   timer->Fire();
     592                 : 
     593            4298 :   return NS_OK;
     594                 : }
     595                 : 
     596            4298 : 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            8596 :   nsRefPtr<nsTimerEvent> event = new nsTimerEvent(this, mGeneration);
     606            4298 :   if (!event)
     607               0 :     return NS_ERROR_OUT_OF_MEMORY;
     608                 : 
     609                 : #ifdef DEBUG_TIMERS
     610            4298 :   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            4298 :   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            4298 :   nsresult rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
     629            4298 :   if (NS_FAILED(rv) && gThread)
     630               0 :     gThread->RemoveTimer(this);
     631            4298 :   return rv;
     632                 : }
     633                 : 
     634           26707 : void nsTimerImpl::SetDelayInternal(PRUint32 aDelay)
     635                 : {
     636           26707 :   TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
     637                 : 
     638           26707 :   mDelay = aDelay;
     639                 : 
     640           26707 :   TimeStamp now = TimeStamp::Now();
     641           26707 :   if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
     642           26706 :     mTimeout = now;
     643                 : 
     644           26707 :   mTimeout += delayInterval;
     645                 : 
     646                 : #ifdef DEBUG_TIMERS
     647           26707 :   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           26707 : }
     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            4392 : }

Generated by: LCOV version 1.7