xpcom/threads/nsTimerImpl.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsTimerImpl.h"
     8 #include "TimerThread.h"
     9 #include "nsAutoPtr.h"
    10 #include "nsThreadManager.h"
    11 #include "nsThreadUtils.h"
    12 #include "plarena.h"
    13 #include "pratom.h"
    14 #include "GeckoProfiler.h"
    15 #include "mozilla/Atomics.h"
    17 using mozilla::Atomic;
    18 using mozilla::TimeDuration;
    19 using mozilla::TimeStamp;
    21 static Atomic<int32_t>  gGenerator;
    22 static TimerThread*     gThread = nullptr;
    24 #ifdef DEBUG_TIMERS
    26 PRLogModuleInfo*
    27 GetTimerLog()
    28 {
    29   static PRLogModuleInfo *sLog;
    30   if (!sLog)
    31     sLog = PR_NewLogModule("nsTimerImpl");
    32   return sLog;
    33 }
    35 #include <math.h>
    37 double nsTimerImpl::sDeltaSumSquared = 0;
    38 double nsTimerImpl::sDeltaSum = 0;
    39 double nsTimerImpl::sDeltaNum = 0;
    41 static void
    42 myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
    43                    double *meanResult, double *stdDevResult)
    44 {
    45   double mean = 0.0, var = 0.0, stdDev = 0.0;
    46   if (n > 0.0 && sumOfValues >= 0) {
    47     mean = sumOfValues / n;
    48     double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
    49     if (temp < 0.0 || n <= 1)
    50       var = 0.0;
    51     else
    52       var = temp / (n * (n - 1));
    53     // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
    54     stdDev = var != 0.0 ? sqrt(var) : 0.0;
    55   }
    56   *meanResult = mean;
    57   *stdDevResult = stdDev;
    58 }
    59 #endif
    61 namespace {
    63 // TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
    64 // It's needed to avoid contention over the default allocator lock when
    65 // firing timer events (see bug 733277).  The thread-safety is required because
    66 // nsTimerEvent objects are allocated on the timer thread, and freed on another
    67 // thread.  Because TimerEventAllocator has its own lock, contention over that
    68 // lock is limited to the allocation and deallocation of nsTimerEvent objects.
    69 //
    70 // Because this allocator is layered over PLArenaPool, it never shrinks -- even
    71 // "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
    72 // for later recycling.  So the amount of memory consumed will always be equal
    73 // to the high-water mark consumption.  But nsTimerEvents are small and it's
    74 // unusual to have more than a few hundred of them, so this shouldn't be a
    75 // problem in practice.
    77 class TimerEventAllocator
    78 {
    79 private:
    80   struct FreeEntry {
    81     FreeEntry* mNext;
    82   };
    84   PLArenaPool mPool;
    85   FreeEntry* mFirstFree;
    86   mozilla::Monitor mMonitor;
    88 public:
    89   TimerEventAllocator()
    90     : mFirstFree(nullptr),
    91       mMonitor("TimerEventAllocator")
    92   {
    93     PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
    94   }
    96   ~TimerEventAllocator()
    97   {
    98     PL_FinishArenaPool(&mPool);
    99   }
   101   void* Alloc(size_t aSize);
   102   void Free(void* aPtr);
   103 };
   105 } // anonymous namespace
   107 class nsTimerEvent : public nsRunnable {
   108 public:
   109   NS_IMETHOD Run();
   111   nsTimerEvent()
   112     : mTimer()
   113     , mGeneration(0)
   114    {
   115     MOZ_COUNT_CTOR(nsTimerEvent);
   117     MOZ_ASSERT(gThread->IsOnTimerThread(),
   118                "nsTimer must always be allocated on the timer thread");
   120     sAllocatorUsers++;
   121   }
   123 #ifdef DEBUG_TIMERS
   124   TimeStamp mInitTime;
   125 #endif
   127   static void Init();
   128   static void Shutdown();
   129   static void DeleteAllocatorIfNeeded();
   131   static void* operator new(size_t size) CPP_THROW_NEW {
   132     return sAllocator->Alloc(size);
   133   }
   134   void operator delete(void* p) {
   135     sAllocator->Free(p);
   136     DeleteAllocatorIfNeeded();
   137   }
   139   already_AddRefed<nsTimerImpl> ForgetTimer()
   140   {
   141     return mTimer.forget();
   142   }
   144   void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
   145   {
   146     mTimer = aTimer;
   147     mGeneration = mTimer->GetGeneration();
   148   }
   150 private:
   151   ~nsTimerEvent() {
   152     MOZ_COUNT_DTOR(nsTimerEvent);
   154     MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
   155                "This will result in us attempting to deallocate the nsTimerEvent allocator twice");
   156     sAllocatorUsers--;
   157   }
   159   nsRefPtr<nsTimerImpl> mTimer;
   160   int32_t      mGeneration;
   162   static TimerEventAllocator* sAllocator;
   163   static Atomic<int32_t> sAllocatorUsers;
   164   static bool sCanDeleteAllocator;
   165 };
   167 TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
   168 Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
   169 bool nsTimerEvent::sCanDeleteAllocator = false;
   171 namespace {
   173 void* TimerEventAllocator::Alloc(size_t aSize)
   174 {
   175   MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
   177   mozilla::MonitorAutoLock lock(mMonitor);
   179   void* p;
   180   if (mFirstFree) {
   181     p = mFirstFree;
   182     mFirstFree = mFirstFree->mNext;
   183   }
   184   else {
   185     PL_ARENA_ALLOCATE(p, &mPool, aSize);
   186     if (!p)
   187       return nullptr;
   188   }
   190   return p;
   191 }
   193 void TimerEventAllocator::Free(void* aPtr)
   194 {
   195   mozilla::MonitorAutoLock lock(mMonitor);
   197   FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
   199   entry->mNext = mFirstFree;
   200   mFirstFree = entry;
   201 }
   203 } // anonymous namespace
   205 NS_IMPL_QUERY_INTERFACE(nsTimerImpl, nsITimer)
   206 NS_IMPL_ADDREF(nsTimerImpl)
   208 NS_IMETHODIMP_(MozExternalRefCountType) nsTimerImpl::Release(void)
   209 {
   210   nsrefcnt count;
   212   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
   213   count = --mRefCnt;
   214   NS_LOG_RELEASE(this, count, "nsTimerImpl");
   215   if (count == 0) {
   216     mRefCnt = 1; /* stabilize */
   218     /* enable this to find non-threadsafe destructors: */
   219     /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
   220     delete this;
   221     return 0;
   222   }
   224   // If only one reference remains, and mArmed is set, then the ref must be
   225   // from the TimerThread::mTimers array, so we Cancel this timer to remove
   226   // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
   227   //
   228   // We use an inlined version of nsTimerImpl::Cancel here to check for the
   229   // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
   230   // timer is not found in the mTimers array -- i.e., when the timer was not
   231   // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
   232   // being true here.  That can happen if the armed timer is being fired by
   233   // TimerThread::Run as we race and test mArmed just before it is cleared by
   234   // the timer thread.  If the RemoveTimer call below doesn't find this timer
   235   // in the mTimers array, then the last ref to this timer is held manually
   236   // and temporarily by the TimerThread, so we should fall through to the
   237   // final return and return 1, not 0.
   238   //
   239   // The original version of this thread-based timer code kept weak refs from
   240   // TimerThread::mTimers, removing this timer's weak ref in the destructor,
   241   // but that leads to double-destructions in the race described above, and
   242   // adding mArmed doesn't help, because destructors can't be deferred, once
   243   // begun.  But by combining reference-counting and a specialized Release
   244   // method with "is this timer still in the mTimers array once we acquire
   245   // the TimerThread's lock" testing, we defer destruction until we're sure
   246   // that only one thread has its hot little hands on this timer.
   247   //
   248   // Note that both approaches preclude a timer creator, and everyone else
   249   // except the TimerThread who might have a strong ref, from dropping all
   250   // their strong refs without implicitly canceling the timer.  Timers need
   251   // non-mTimers-element strong refs to stay alive.
   253   if (count == 1 && mArmed) {
   254     mCanceled = true;
   256     MOZ_ASSERT(gThread, "Armed timer exists after the thread timer stopped.");
   257     if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
   258       return 0;
   259   }
   261   return count;
   262 }
   264 nsTimerImpl::nsTimerImpl() :
   265   mClosure(nullptr),
   266   mCallbackType(CALLBACK_TYPE_UNKNOWN),
   267   mFiring(false),
   268   mArmed(false),
   269   mCanceled(false),
   270   mGeneration(0),
   271   mDelay(0)
   272 {
   273   // XXXbsmedberg: shouldn't this be in Init()?
   274   mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   276   mCallback.c = nullptr;
   277 }
   279 nsTimerImpl::~nsTimerImpl()
   280 {
   281   ReleaseCallback();
   282 }
   284 //static
   285 nsresult
   286 nsTimerImpl::Startup()
   287 {
   288   nsresult rv;
   290   nsTimerEvent::Init();
   292   gThread = new TimerThread();
   293   if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
   295   NS_ADDREF(gThread);
   296   rv = gThread->InitLocks();
   298   if (NS_FAILED(rv)) {
   299     NS_RELEASE(gThread);
   300   }
   302   return rv;
   303 }
   305 void nsTimerImpl::Shutdown()
   306 {
   307 #ifdef DEBUG_TIMERS
   308   if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   309     double mean = 0, stddev = 0;
   310     myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
   312     PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
   313     PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
   314   }
   315 #endif
   317   if (!gThread)
   318     return;
   320   gThread->Shutdown();
   321   NS_RELEASE(gThread);
   323   nsTimerEvent::Shutdown();
   324 }
   327 nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
   328 {
   329   nsresult rv;
   331   if (NS_WARN_IF(!gThread))
   332     return NS_ERROR_NOT_INITIALIZED;
   333   if (!mEventTarget) {
   334     NS_ERROR("mEventTarget is NULL");
   335     return NS_ERROR_NOT_INITIALIZED;
   336   }
   338   rv = gThread->Init();
   339   if (NS_WARN_IF(NS_FAILED(rv)))
   340     return rv;
   342   /**
   343    * In case of re-Init, both with and without a preceding Cancel, clear the
   344    * mCanceled flag and assign a new mGeneration.  But first, remove any armed
   345    * timer from the timer thread's list.
   346    *
   347    * If we are racing with the timer thread to remove this timer and we lose,
   348    * the RemoveTimer call made here will fail to find this timer in the timer
   349    * thread's list, and will return false harmlessly.  We test mArmed here to
   350    * avoid the small overhead in RemoveTimer of locking the timer thread and
   351    * checking its list for this timer.  It's safe to test mArmed even though
   352    * it might be cleared on another thread in the next cycle (or even already
   353    * be cleared by another CPU whose store hasn't reached our CPU's cache),
   354    * because RemoveTimer is idempotent.
   355    */
   356   if (mArmed)
   357     gThread->RemoveTimer(this);
   358   mCanceled = false;
   359   mTimeout = TimeStamp();
   360   mGeneration = gGenerator++;
   362   mType = (uint8_t)aType;
   363   SetDelayInternal(aDelay);
   365   return gThread->AddTimer(this);
   366 }
   368 NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
   369                                                 void *aClosure,
   370                                                 uint32_t aDelay,
   371                                                 uint32_t aType)
   372 {
   373   if (NS_WARN_IF(!aFunc))
   374     return NS_ERROR_INVALID_ARG;
   376   ReleaseCallback();
   377   mCallbackType = CALLBACK_TYPE_FUNC;
   378   mCallback.c = aFunc;
   379   mClosure = aClosure;
   381   return InitCommon(aType, aDelay);
   382 }
   384 NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
   385                                             uint32_t aDelay,
   386                                             uint32_t aType)
   387 {
   388   if (NS_WARN_IF(!aCallback))
   389     return NS_ERROR_INVALID_ARG;
   391   ReleaseCallback();
   392   mCallbackType = CALLBACK_TYPE_INTERFACE;
   393   mCallback.i = aCallback;
   394   NS_ADDREF(mCallback.i);
   396   return InitCommon(aType, aDelay);
   397 }
   399 NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
   400                                 uint32_t aDelay,
   401                                 uint32_t aType)
   402 {
   403   if (NS_WARN_IF(!aObserver))
   404     return NS_ERROR_INVALID_ARG;
   406   ReleaseCallback();
   407   mCallbackType = CALLBACK_TYPE_OBSERVER;
   408   mCallback.o = aObserver;
   409   NS_ADDREF(mCallback.o);
   411   return InitCommon(aType, aDelay);
   412 }
   414 NS_IMETHODIMP nsTimerImpl::Cancel()
   415 {
   416   mCanceled = true;
   418   if (gThread)
   419     gThread->RemoveTimer(this);
   421   ReleaseCallback();
   423   return NS_OK;
   424 }
   426 NS_IMETHODIMP nsTimerImpl::SetDelay(uint32_t aDelay)
   427 {
   428   if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) {
   429     // This may happen if someone tries to re-use a one-shot timer
   430     // by re-setting delay instead of reinitializing the timer.
   431     NS_ERROR("nsITimer->SetDelay() called when the "
   432              "one-shot timer is not set up.");
   433     return NS_ERROR_NOT_INITIALIZED;
   434   }
   436   // If we're already repeating precisely, update mTimeout now so that the
   437   // new delay takes effect in the future.
   438   if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
   439     mTimeout = TimeStamp::Now();
   441   SetDelayInternal(aDelay);
   443   if (!mFiring && gThread)
   444     gThread->TimerDelayChanged(this);
   446   return NS_OK;
   447 }
   449 NS_IMETHODIMP nsTimerImpl::GetDelay(uint32_t* aDelay)
   450 {
   451   *aDelay = mDelay;
   452   return NS_OK;
   453 }
   455 NS_IMETHODIMP nsTimerImpl::SetType(uint32_t aType)
   456 {
   457   mType = (uint8_t)aType;
   458   // XXX if this is called, we should change the actual type.. this could effect
   459   // repeating timers.  we need to ensure in Fire() that if mType has changed
   460   // during the callback that we don't end up with the timer in the queue twice.
   461   return NS_OK;
   462 }
   464 NS_IMETHODIMP nsTimerImpl::GetType(uint32_t* aType)
   465 {
   466   *aType = mType;
   467   return NS_OK;
   468 }
   471 NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
   472 {
   473   *aClosure = mClosure;
   474   return NS_OK;
   475 }
   478 NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
   479 {
   480   if (mCallbackType == CALLBACK_TYPE_INTERFACE)
   481     NS_IF_ADDREF(*aCallback = mCallback.i);
   482   else if (mTimerCallbackWhileFiring)
   483     NS_ADDREF(*aCallback = mTimerCallbackWhileFiring);
   484   else
   485     *aCallback = nullptr;
   487   return NS_OK;
   488 }
   491 NS_IMETHODIMP nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
   492 {
   493   NS_IF_ADDREF(*aTarget = mEventTarget);
   494   return NS_OK;
   495 }
   498 NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
   499 {
   500   if (NS_WARN_IF(mCallbackType != CALLBACK_TYPE_UNKNOWN))
   501     return NS_ERROR_ALREADY_INITIALIZED;
   503   if (aTarget)
   504     mEventTarget = aTarget;
   505   else
   506     mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   507   return NS_OK;
   508 }
   511 void nsTimerImpl::Fire()
   512 {
   513   if (mCanceled)
   514     return;
   516   PROFILER_LABEL("Timer", "Fire");
   518 #ifdef MOZ_TASK_TRACER
   519   mozilla::tasktracer::AutoRunFakeTracedTask runTracedTask(mTracedTask);
   520 #endif
   522 #ifdef DEBUG_TIMERS
   523   TimeStamp now = TimeStamp::Now();
   524   if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   525     TimeDuration   a = now - mStart; // actual delay in intervals
   526     TimeDuration   b = TimeDuration::FromMilliseconds(mDelay); // expected delay in intervals
   527     TimeDuration   delta = (a > b) ? a - b : b - a;
   528     uint32_t       d = delta.ToMilliseconds(); // delta in ms
   529     sDeltaSum += d;
   530     sDeltaSumSquared += double(d) * double(d);
   531     sDeltaNum++;
   533     PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] expected delay time %4ums\n", this, mDelay));
   534     PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] actual delay time   %fms\n", this, a.ToMilliseconds()));
   535     PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] (mType is %d)       -------\n", this, mType));
   536     PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p]     delta           %4dms\n", this, (a > b) ? (int32_t)d : -(int32_t)d));
   538     mStart = mStart2;
   539     mStart2 = TimeStamp();
   540   }
   541 #endif
   543   TimeStamp timeout = mTimeout;
   544   if (IsRepeatingPrecisely()) {
   545     // Precise repeating timers advance mTimeout by mDelay without fail before
   546     // calling Fire().
   547     timeout -= TimeDuration::FromMilliseconds(mDelay);
   548   }
   550   if (mCallbackType == CALLBACK_TYPE_INTERFACE)
   551     mTimerCallbackWhileFiring = mCallback.i;
   552   mFiring = true;
   554   // Handle callbacks that re-init the timer, but avoid leaking.
   555   // See bug 330128.
   556   CallbackUnion callback = mCallback;
   557   unsigned callbackType = mCallbackType;
   558   if (callbackType == CALLBACK_TYPE_INTERFACE)
   559     NS_ADDREF(callback.i);
   560   else if (callbackType == CALLBACK_TYPE_OBSERVER)
   561     NS_ADDREF(callback.o);
   562   ReleaseCallback();
   564   switch (callbackType) {
   565     case CALLBACK_TYPE_FUNC:
   566       callback.c(this, mClosure);
   567       break;
   568     case CALLBACK_TYPE_INTERFACE:
   569       callback.i->Notify(this);
   570       break;
   571     case CALLBACK_TYPE_OBSERVER:
   572       callback.o->Observe(static_cast<nsITimer*>(this),
   573                           NS_TIMER_CALLBACK_TOPIC,
   574                           nullptr);
   575       break;
   576     default:;
   577   }
   579   // If the callback didn't re-init the timer, and it's not a one-shot timer,
   580   // restore the callback state.
   581   if (mCallbackType == CALLBACK_TYPE_UNKNOWN &&
   582       mType != TYPE_ONE_SHOT && !mCanceled) {
   583     mCallback = callback;
   584     mCallbackType = callbackType;
   585   } else {
   586     // The timer was a one-shot, or the callback was reinitialized.
   587     if (callbackType == CALLBACK_TYPE_INTERFACE)
   588       NS_RELEASE(callback.i);
   589     else if (callbackType == CALLBACK_TYPE_OBSERVER)
   590       NS_RELEASE(callback.o);
   591   }
   593   mFiring = false;
   594   mTimerCallbackWhileFiring = nullptr;
   596 #ifdef DEBUG_TIMERS
   597   if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   598     PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
   599            ("[this=%p] Took %fms to fire timer callback\n",
   600             this, (TimeStamp::Now() - now).ToMilliseconds()));
   601   }
   602 #endif
   604   // Reschedule repeating timers, except REPEATING_PRECISE which already did
   605   // that in PostTimerEvent, but make sure that we aren't armed already (which
   606   // can happen if the callback reinitialized the timer).
   607   if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
   608     if (mType == TYPE_REPEATING_SLACK)
   609       SetDelayInternal(mDelay); // force mTimeout to be recomputed.  For
   610                                 // REPEATING_PRECISE_CAN_SKIP timers this has
   611                                 // already happened.
   612     if (gThread)
   613       gThread->AddTimer(this);
   614   }
   615 }
   617 void nsTimerEvent::Init()
   618 {
   619   sAllocator = new TimerEventAllocator();
   620 }
   622 void nsTimerEvent::Shutdown()
   623 {
   624   sCanDeleteAllocator = true;
   625   DeleteAllocatorIfNeeded();
   626 }
   628 void nsTimerEvent::DeleteAllocatorIfNeeded()
   629 {
   630   if (sCanDeleteAllocator && sAllocatorUsers == 0) {
   631     delete sAllocator;
   632     sAllocator = nullptr;
   633   }
   634 }
   636 NS_IMETHODIMP nsTimerEvent::Run()
   637 {
   638   if (mGeneration != mTimer->GetGeneration())
   639     return NS_OK;
   641 #ifdef DEBUG_TIMERS
   642   if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   643     TimeStamp now = TimeStamp::Now();
   644     PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
   645            ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
   646             this, (now - mInitTime).ToMilliseconds()));
   647   }
   648 #endif
   650   mTimer->Fire();
   651   // Since nsTimerImpl is not thread-safe, we should release |mTimer|
   652   // here in the target thread to avoid race condition. Otherwise,
   653   // ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
   654   // timer thread and result in race condition.
   655   mTimer = nullptr;
   657   return NS_OK;
   658 }
   660 already_AddRefed<nsTimerImpl>
   661 nsTimerImpl::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
   662 {
   663   nsRefPtr<nsTimerImpl> timer(aTimerRef);
   664   if (!timer->mEventTarget) {
   665     NS_ERROR("Attempt to post timer event to NULL event target");
   666     return timer.forget();
   667   }
   669   // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
   671   // Since TimerThread addref'd 'timer' for us, we don't need to addref here.
   672   // We will release either in ~nsTimerEvent(), or pass the reference back to
   673   // the caller. We need to copy the generation number from this timer into the
   674   // event, so we can avoid firing a timer that was re-initialized after being
   675   // canceled.
   677   // Note: We override operator new for this class, and the override is
   678   // fallible!
   679   nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
   680   if (!event)
   681     return timer.forget();
   683 #ifdef DEBUG_TIMERS
   684   if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   685     event->mInitTime = TimeStamp::Now();
   686   }
   687 #endif
   689   // If this is a repeating precise timer, we need to calculate the time for
   690   // the next timer to fire before we make the callback.
   691   if (timer->IsRepeatingPrecisely()) {
   692     timer->SetDelayInternal(timer->mDelay);
   694     // But only re-arm REPEATING_PRECISE timers.
   695     if (gThread && timer->mType == TYPE_REPEATING_PRECISE) {
   696       nsresult rv = gThread->AddTimer(timer);
   697       if (NS_FAILED(rv)) {
   698         return timer.forget();
   699       }
   700     }
   701   }
   703   nsIEventTarget* target = timer->mEventTarget;
   704   event->SetTimer(timer.forget());
   706   nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
   707   if (NS_FAILED(rv)) {
   708     timer = event->ForgetTimer();
   709     if (gThread) {
   710       gThread->RemoveTimer(timer);
   711     }
   712     return timer.forget();
   713   }
   715   return nullptr;
   716 }
   718 void nsTimerImpl::SetDelayInternal(uint32_t aDelay)
   719 {
   720   TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
   722   mDelay = aDelay;
   724   TimeStamp now = TimeStamp::Now();
   725   if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
   726     mTimeout = now;
   728   mTimeout += delayInterval;
   730 #ifdef DEBUG_TIMERS
   731   if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   732     if (mStart.IsNull())
   733       mStart = now;
   734     else
   735       mStart2 = now;
   736   }
   737 #endif
   738 }
   740 // NOT FOR PUBLIC CONSUMPTION!
   741 nsresult
   742 NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
   743             uint32_t aDelay, uint32_t aType)
   744 {
   745     nsTimerImpl* timer = new nsTimerImpl();
   746     if (timer == nullptr)
   747         return NS_ERROR_OUT_OF_MEMORY;
   748     NS_ADDREF(timer);
   750     nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure, 
   751                                               aDelay, aType);
   752     if (NS_FAILED(rv)) {
   753         NS_RELEASE(timer);
   754         return rv;
   755     }
   757     *aResult = timer;
   758     return NS_OK;
   759 }

mercurial