xpcom/threads/nsTimerImpl.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/threads/nsTimerImpl.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,759 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsTimerImpl.h"
    1.11 +#include "TimerThread.h"
    1.12 +#include "nsAutoPtr.h"
    1.13 +#include "nsThreadManager.h"
    1.14 +#include "nsThreadUtils.h"
    1.15 +#include "plarena.h"
    1.16 +#include "pratom.h"
    1.17 +#include "GeckoProfiler.h"
    1.18 +#include "mozilla/Atomics.h"
    1.19 +
    1.20 +using mozilla::Atomic;
    1.21 +using mozilla::TimeDuration;
    1.22 +using mozilla::TimeStamp;
    1.23 +
    1.24 +static Atomic<int32_t>  gGenerator;
    1.25 +static TimerThread*     gThread = nullptr;
    1.26 +
    1.27 +#ifdef DEBUG_TIMERS
    1.28 +
    1.29 +PRLogModuleInfo*
    1.30 +GetTimerLog()
    1.31 +{
    1.32 +  static PRLogModuleInfo *sLog;
    1.33 +  if (!sLog)
    1.34 +    sLog = PR_NewLogModule("nsTimerImpl");
    1.35 +  return sLog;
    1.36 +}
    1.37 +
    1.38 +#include <math.h>
    1.39 +
    1.40 +double nsTimerImpl::sDeltaSumSquared = 0;
    1.41 +double nsTimerImpl::sDeltaSum = 0;
    1.42 +double nsTimerImpl::sDeltaNum = 0;
    1.43 +
    1.44 +static void
    1.45 +myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
    1.46 +                   double *meanResult, double *stdDevResult)
    1.47 +{
    1.48 +  double mean = 0.0, var = 0.0, stdDev = 0.0;
    1.49 +  if (n > 0.0 && sumOfValues >= 0) {
    1.50 +    mean = sumOfValues / n;
    1.51 +    double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
    1.52 +    if (temp < 0.0 || n <= 1)
    1.53 +      var = 0.0;
    1.54 +    else
    1.55 +      var = temp / (n * (n - 1));
    1.56 +    // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
    1.57 +    stdDev = var != 0.0 ? sqrt(var) : 0.0;
    1.58 +  }
    1.59 +  *meanResult = mean;
    1.60 +  *stdDevResult = stdDev;
    1.61 +}
    1.62 +#endif
    1.63 +
    1.64 +namespace {
    1.65 +
    1.66 +// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
    1.67 +// It's needed to avoid contention over the default allocator lock when
    1.68 +// firing timer events (see bug 733277).  The thread-safety is required because
    1.69 +// nsTimerEvent objects are allocated on the timer thread, and freed on another
    1.70 +// thread.  Because TimerEventAllocator has its own lock, contention over that
    1.71 +// lock is limited to the allocation and deallocation of nsTimerEvent objects.
    1.72 +//
    1.73 +// Because this allocator is layered over PLArenaPool, it never shrinks -- even
    1.74 +// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
    1.75 +// for later recycling.  So the amount of memory consumed will always be equal
    1.76 +// to the high-water mark consumption.  But nsTimerEvents are small and it's
    1.77 +// unusual to have more than a few hundred of them, so this shouldn't be a
    1.78 +// problem in practice.
    1.79 +
    1.80 +class TimerEventAllocator
    1.81 +{
    1.82 +private:
    1.83 +  struct FreeEntry {
    1.84 +    FreeEntry* mNext;
    1.85 +  };
    1.86 +
    1.87 +  PLArenaPool mPool;
    1.88 +  FreeEntry* mFirstFree;
    1.89 +  mozilla::Monitor mMonitor;
    1.90 +
    1.91 +public:
    1.92 +  TimerEventAllocator()
    1.93 +    : mFirstFree(nullptr),
    1.94 +      mMonitor("TimerEventAllocator")
    1.95 +  {
    1.96 +    PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
    1.97 +  }
    1.98 +
    1.99 +  ~TimerEventAllocator()
   1.100 +  {
   1.101 +    PL_FinishArenaPool(&mPool);
   1.102 +  }
   1.103 +
   1.104 +  void* Alloc(size_t aSize);
   1.105 +  void Free(void* aPtr);
   1.106 +};
   1.107 +
   1.108 +} // anonymous namespace
   1.109 +
   1.110 +class nsTimerEvent : public nsRunnable {
   1.111 +public:
   1.112 +  NS_IMETHOD Run();
   1.113 +
   1.114 +  nsTimerEvent()
   1.115 +    : mTimer()
   1.116 +    , mGeneration(0)
   1.117 +   {
   1.118 +    MOZ_COUNT_CTOR(nsTimerEvent);
   1.119 +
   1.120 +    MOZ_ASSERT(gThread->IsOnTimerThread(),
   1.121 +               "nsTimer must always be allocated on the timer thread");
   1.122 +
   1.123 +    sAllocatorUsers++;
   1.124 +  }
   1.125 +
   1.126 +#ifdef DEBUG_TIMERS
   1.127 +  TimeStamp mInitTime;
   1.128 +#endif
   1.129 +
   1.130 +  static void Init();
   1.131 +  static void Shutdown();
   1.132 +  static void DeleteAllocatorIfNeeded();
   1.133 +
   1.134 +  static void* operator new(size_t size) CPP_THROW_NEW {
   1.135 +    return sAllocator->Alloc(size);
   1.136 +  }
   1.137 +  void operator delete(void* p) {
   1.138 +    sAllocator->Free(p);
   1.139 +    DeleteAllocatorIfNeeded();
   1.140 +  }
   1.141 +
   1.142 +  already_AddRefed<nsTimerImpl> ForgetTimer()
   1.143 +  {
   1.144 +    return mTimer.forget();
   1.145 +  }
   1.146 +
   1.147 +  void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
   1.148 +  {
   1.149 +    mTimer = aTimer;
   1.150 +    mGeneration = mTimer->GetGeneration();
   1.151 +  }
   1.152 +
   1.153 +private:
   1.154 +  ~nsTimerEvent() {
   1.155 +    MOZ_COUNT_DTOR(nsTimerEvent);
   1.156 +
   1.157 +    MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
   1.158 +               "This will result in us attempting to deallocate the nsTimerEvent allocator twice");
   1.159 +    sAllocatorUsers--;
   1.160 +  }
   1.161 +
   1.162 +  nsRefPtr<nsTimerImpl> mTimer;
   1.163 +  int32_t      mGeneration;
   1.164 +
   1.165 +  static TimerEventAllocator* sAllocator;
   1.166 +  static Atomic<int32_t> sAllocatorUsers;
   1.167 +  static bool sCanDeleteAllocator;
   1.168 +};
   1.169 +
   1.170 +TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
   1.171 +Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
   1.172 +bool nsTimerEvent::sCanDeleteAllocator = false;
   1.173 +
   1.174 +namespace {
   1.175 +
   1.176 +void* TimerEventAllocator::Alloc(size_t aSize)
   1.177 +{
   1.178 +  MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
   1.179 +
   1.180 +  mozilla::MonitorAutoLock lock(mMonitor);
   1.181 +
   1.182 +  void* p;
   1.183 +  if (mFirstFree) {
   1.184 +    p = mFirstFree;
   1.185 +    mFirstFree = mFirstFree->mNext;
   1.186 +  }
   1.187 +  else {
   1.188 +    PL_ARENA_ALLOCATE(p, &mPool, aSize);
   1.189 +    if (!p)
   1.190 +      return nullptr;
   1.191 +  }
   1.192 +
   1.193 +  return p;
   1.194 +}
   1.195 +
   1.196 +void TimerEventAllocator::Free(void* aPtr)
   1.197 +{
   1.198 +  mozilla::MonitorAutoLock lock(mMonitor);
   1.199 +
   1.200 +  FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
   1.201 +
   1.202 +  entry->mNext = mFirstFree;
   1.203 +  mFirstFree = entry;
   1.204 +}
   1.205 +
   1.206 +} // anonymous namespace
   1.207 +
   1.208 +NS_IMPL_QUERY_INTERFACE(nsTimerImpl, nsITimer)
   1.209 +NS_IMPL_ADDREF(nsTimerImpl)
   1.210 +
   1.211 +NS_IMETHODIMP_(MozExternalRefCountType) nsTimerImpl::Release(void)
   1.212 +{
   1.213 +  nsrefcnt count;
   1.214 +
   1.215 +  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
   1.216 +  count = --mRefCnt;
   1.217 +  NS_LOG_RELEASE(this, count, "nsTimerImpl");
   1.218 +  if (count == 0) {
   1.219 +    mRefCnt = 1; /* stabilize */
   1.220 +
   1.221 +    /* enable this to find non-threadsafe destructors: */
   1.222 +    /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
   1.223 +    delete this;
   1.224 +    return 0;
   1.225 +  }
   1.226 +
   1.227 +  // If only one reference remains, and mArmed is set, then the ref must be
   1.228 +  // from the TimerThread::mTimers array, so we Cancel this timer to remove
   1.229 +  // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
   1.230 +  //
   1.231 +  // We use an inlined version of nsTimerImpl::Cancel here to check for the
   1.232 +  // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
   1.233 +  // timer is not found in the mTimers array -- i.e., when the timer was not
   1.234 +  // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
   1.235 +  // being true here.  That can happen if the armed timer is being fired by
   1.236 +  // TimerThread::Run as we race and test mArmed just before it is cleared by
   1.237 +  // the timer thread.  If the RemoveTimer call below doesn't find this timer
   1.238 +  // in the mTimers array, then the last ref to this timer is held manually
   1.239 +  // and temporarily by the TimerThread, so we should fall through to the
   1.240 +  // final return and return 1, not 0.
   1.241 +  //
   1.242 +  // The original version of this thread-based timer code kept weak refs from
   1.243 +  // TimerThread::mTimers, removing this timer's weak ref in the destructor,
   1.244 +  // but that leads to double-destructions in the race described above, and
   1.245 +  // adding mArmed doesn't help, because destructors can't be deferred, once
   1.246 +  // begun.  But by combining reference-counting and a specialized Release
   1.247 +  // method with "is this timer still in the mTimers array once we acquire
   1.248 +  // the TimerThread's lock" testing, we defer destruction until we're sure
   1.249 +  // that only one thread has its hot little hands on this timer.
   1.250 +  //
   1.251 +  // Note that both approaches preclude a timer creator, and everyone else
   1.252 +  // except the TimerThread who might have a strong ref, from dropping all
   1.253 +  // their strong refs without implicitly canceling the timer.  Timers need
   1.254 +  // non-mTimers-element strong refs to stay alive.
   1.255 +
   1.256 +  if (count == 1 && mArmed) {
   1.257 +    mCanceled = true;
   1.258 +
   1.259 +    MOZ_ASSERT(gThread, "Armed timer exists after the thread timer stopped.");
   1.260 +    if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
   1.261 +      return 0;
   1.262 +  }
   1.263 +
   1.264 +  return count;
   1.265 +}
   1.266 +
   1.267 +nsTimerImpl::nsTimerImpl() :
   1.268 +  mClosure(nullptr),
   1.269 +  mCallbackType(CALLBACK_TYPE_UNKNOWN),
   1.270 +  mFiring(false),
   1.271 +  mArmed(false),
   1.272 +  mCanceled(false),
   1.273 +  mGeneration(0),
   1.274 +  mDelay(0)
   1.275 +{
   1.276 +  // XXXbsmedberg: shouldn't this be in Init()?
   1.277 +  mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.278 +
   1.279 +  mCallback.c = nullptr;
   1.280 +}
   1.281 +
   1.282 +nsTimerImpl::~nsTimerImpl()
   1.283 +{
   1.284 +  ReleaseCallback();
   1.285 +}
   1.286 +
   1.287 +//static
   1.288 +nsresult
   1.289 +nsTimerImpl::Startup()
   1.290 +{
   1.291 +  nsresult rv;
   1.292 +
   1.293 +  nsTimerEvent::Init();
   1.294 +
   1.295 +  gThread = new TimerThread();
   1.296 +  if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
   1.297 +
   1.298 +  NS_ADDREF(gThread);
   1.299 +  rv = gThread->InitLocks();
   1.300 +
   1.301 +  if (NS_FAILED(rv)) {
   1.302 +    NS_RELEASE(gThread);
   1.303 +  }
   1.304 +
   1.305 +  return rv;
   1.306 +}
   1.307 +
   1.308 +void nsTimerImpl::Shutdown()
   1.309 +{
   1.310 +#ifdef DEBUG_TIMERS
   1.311 +  if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   1.312 +    double mean = 0, stddev = 0;
   1.313 +    myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
   1.314 +
   1.315 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
   1.316 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
   1.317 +  }
   1.318 +#endif
   1.319 +
   1.320 +  if (!gThread)
   1.321 +    return;
   1.322 +
   1.323 +  gThread->Shutdown();
   1.324 +  NS_RELEASE(gThread);
   1.325 +
   1.326 +  nsTimerEvent::Shutdown();
   1.327 +}
   1.328 +
   1.329 +
   1.330 +nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
   1.331 +{
   1.332 +  nsresult rv;
   1.333 +
   1.334 +  if (NS_WARN_IF(!gThread))
   1.335 +    return NS_ERROR_NOT_INITIALIZED;
   1.336 +  if (!mEventTarget) {
   1.337 +    NS_ERROR("mEventTarget is NULL");
   1.338 +    return NS_ERROR_NOT_INITIALIZED;
   1.339 +  }
   1.340 +
   1.341 +  rv = gThread->Init();
   1.342 +  if (NS_WARN_IF(NS_FAILED(rv)))
   1.343 +    return rv;
   1.344 +
   1.345 +  /**
   1.346 +   * In case of re-Init, both with and without a preceding Cancel, clear the
   1.347 +   * mCanceled flag and assign a new mGeneration.  But first, remove any armed
   1.348 +   * timer from the timer thread's list.
   1.349 +   *
   1.350 +   * If we are racing with the timer thread to remove this timer and we lose,
   1.351 +   * the RemoveTimer call made here will fail to find this timer in the timer
   1.352 +   * thread's list, and will return false harmlessly.  We test mArmed here to
   1.353 +   * avoid the small overhead in RemoveTimer of locking the timer thread and
   1.354 +   * checking its list for this timer.  It's safe to test mArmed even though
   1.355 +   * it might be cleared on another thread in the next cycle (or even already
   1.356 +   * be cleared by another CPU whose store hasn't reached our CPU's cache),
   1.357 +   * because RemoveTimer is idempotent.
   1.358 +   */
   1.359 +  if (mArmed)
   1.360 +    gThread->RemoveTimer(this);
   1.361 +  mCanceled = false;
   1.362 +  mTimeout = TimeStamp();
   1.363 +  mGeneration = gGenerator++;
   1.364 +
   1.365 +  mType = (uint8_t)aType;
   1.366 +  SetDelayInternal(aDelay);
   1.367 +
   1.368 +  return gThread->AddTimer(this);
   1.369 +}
   1.370 +
   1.371 +NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
   1.372 +                                                void *aClosure,
   1.373 +                                                uint32_t aDelay,
   1.374 +                                                uint32_t aType)
   1.375 +{
   1.376 +  if (NS_WARN_IF(!aFunc))
   1.377 +    return NS_ERROR_INVALID_ARG;
   1.378 +  
   1.379 +  ReleaseCallback();
   1.380 +  mCallbackType = CALLBACK_TYPE_FUNC;
   1.381 +  mCallback.c = aFunc;
   1.382 +  mClosure = aClosure;
   1.383 +
   1.384 +  return InitCommon(aType, aDelay);
   1.385 +}
   1.386 +
   1.387 +NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
   1.388 +                                            uint32_t aDelay,
   1.389 +                                            uint32_t aType)
   1.390 +{
   1.391 +  if (NS_WARN_IF(!aCallback))
   1.392 +    return NS_ERROR_INVALID_ARG;
   1.393 +
   1.394 +  ReleaseCallback();
   1.395 +  mCallbackType = CALLBACK_TYPE_INTERFACE;
   1.396 +  mCallback.i = aCallback;
   1.397 +  NS_ADDREF(mCallback.i);
   1.398 +
   1.399 +  return InitCommon(aType, aDelay);
   1.400 +}
   1.401 +
   1.402 +NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
   1.403 +                                uint32_t aDelay,
   1.404 +                                uint32_t aType)
   1.405 +{
   1.406 +  if (NS_WARN_IF(!aObserver))
   1.407 +    return NS_ERROR_INVALID_ARG;
   1.408 +
   1.409 +  ReleaseCallback();
   1.410 +  mCallbackType = CALLBACK_TYPE_OBSERVER;
   1.411 +  mCallback.o = aObserver;
   1.412 +  NS_ADDREF(mCallback.o);
   1.413 +
   1.414 +  return InitCommon(aType, aDelay);
   1.415 +}
   1.416 +
   1.417 +NS_IMETHODIMP nsTimerImpl::Cancel()
   1.418 +{
   1.419 +  mCanceled = true;
   1.420 +
   1.421 +  if (gThread)
   1.422 +    gThread->RemoveTimer(this);
   1.423 +
   1.424 +  ReleaseCallback();
   1.425 +
   1.426 +  return NS_OK;
   1.427 +}
   1.428 +
   1.429 +NS_IMETHODIMP nsTimerImpl::SetDelay(uint32_t aDelay)
   1.430 +{
   1.431 +  if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) {
   1.432 +    // This may happen if someone tries to re-use a one-shot timer
   1.433 +    // by re-setting delay instead of reinitializing the timer.
   1.434 +    NS_ERROR("nsITimer->SetDelay() called when the "
   1.435 +             "one-shot timer is not set up.");
   1.436 +    return NS_ERROR_NOT_INITIALIZED;
   1.437 +  }
   1.438 +
   1.439 +  // If we're already repeating precisely, update mTimeout now so that the
   1.440 +  // new delay takes effect in the future.
   1.441 +  if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
   1.442 +    mTimeout = TimeStamp::Now();
   1.443 +
   1.444 +  SetDelayInternal(aDelay);
   1.445 +
   1.446 +  if (!mFiring && gThread)
   1.447 +    gThread->TimerDelayChanged(this);
   1.448 +
   1.449 +  return NS_OK;
   1.450 +}
   1.451 +
   1.452 +NS_IMETHODIMP nsTimerImpl::GetDelay(uint32_t* aDelay)
   1.453 +{
   1.454 +  *aDelay = mDelay;
   1.455 +  return NS_OK;
   1.456 +}
   1.457 +
   1.458 +NS_IMETHODIMP nsTimerImpl::SetType(uint32_t aType)
   1.459 +{
   1.460 +  mType = (uint8_t)aType;
   1.461 +  // XXX if this is called, we should change the actual type.. this could effect
   1.462 +  // repeating timers.  we need to ensure in Fire() that if mType has changed
   1.463 +  // during the callback that we don't end up with the timer in the queue twice.
   1.464 +  return NS_OK;
   1.465 +}
   1.466 +
   1.467 +NS_IMETHODIMP nsTimerImpl::GetType(uint32_t* aType)
   1.468 +{
   1.469 +  *aType = mType;
   1.470 +  return NS_OK;
   1.471 +}
   1.472 +
   1.473 +
   1.474 +NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
   1.475 +{
   1.476 +  *aClosure = mClosure;
   1.477 +  return NS_OK;
   1.478 +}
   1.479 +
   1.480 +
   1.481 +NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
   1.482 +{
   1.483 +  if (mCallbackType == CALLBACK_TYPE_INTERFACE)
   1.484 +    NS_IF_ADDREF(*aCallback = mCallback.i);
   1.485 +  else if (mTimerCallbackWhileFiring)
   1.486 +    NS_ADDREF(*aCallback = mTimerCallbackWhileFiring);
   1.487 +  else
   1.488 +    *aCallback = nullptr;
   1.489 +
   1.490 +  return NS_OK;
   1.491 +}
   1.492 +
   1.493 +
   1.494 +NS_IMETHODIMP nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
   1.495 +{
   1.496 +  NS_IF_ADDREF(*aTarget = mEventTarget);
   1.497 +  return NS_OK;
   1.498 +}
   1.499 +
   1.500 +
   1.501 +NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
   1.502 +{
   1.503 +  if (NS_WARN_IF(mCallbackType != CALLBACK_TYPE_UNKNOWN))
   1.504 +    return NS_ERROR_ALREADY_INITIALIZED;
   1.505 +
   1.506 +  if (aTarget)
   1.507 +    mEventTarget = aTarget;
   1.508 +  else
   1.509 +    mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   1.510 +  return NS_OK;
   1.511 +}
   1.512 +
   1.513 +
   1.514 +void nsTimerImpl::Fire()
   1.515 +{
   1.516 +  if (mCanceled)
   1.517 +    return;
   1.518 +
   1.519 +  PROFILER_LABEL("Timer", "Fire");
   1.520 +
   1.521 +#ifdef MOZ_TASK_TRACER
   1.522 +  mozilla::tasktracer::AutoRunFakeTracedTask runTracedTask(mTracedTask);
   1.523 +#endif
   1.524 +
   1.525 +#ifdef DEBUG_TIMERS
   1.526 +  TimeStamp now = TimeStamp::Now();
   1.527 +  if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   1.528 +    TimeDuration   a = now - mStart; // actual delay in intervals
   1.529 +    TimeDuration   b = TimeDuration::FromMilliseconds(mDelay); // expected delay in intervals
   1.530 +    TimeDuration   delta = (a > b) ? a - b : b - a;
   1.531 +    uint32_t       d = delta.ToMilliseconds(); // delta in ms
   1.532 +    sDeltaSum += d;
   1.533 +    sDeltaSumSquared += double(d) * double(d);
   1.534 +    sDeltaNum++;
   1.535 +
   1.536 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] expected delay time %4ums\n", this, mDelay));
   1.537 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] actual delay time   %fms\n", this, a.ToMilliseconds()));
   1.538 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] (mType is %d)       -------\n", this, mType));
   1.539 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p]     delta           %4dms\n", this, (a > b) ? (int32_t)d : -(int32_t)d));
   1.540 +
   1.541 +    mStart = mStart2;
   1.542 +    mStart2 = TimeStamp();
   1.543 +  }
   1.544 +#endif
   1.545 +
   1.546 +  TimeStamp timeout = mTimeout;
   1.547 +  if (IsRepeatingPrecisely()) {
   1.548 +    // Precise repeating timers advance mTimeout by mDelay without fail before
   1.549 +    // calling Fire().
   1.550 +    timeout -= TimeDuration::FromMilliseconds(mDelay);
   1.551 +  }
   1.552 +
   1.553 +  if (mCallbackType == CALLBACK_TYPE_INTERFACE)
   1.554 +    mTimerCallbackWhileFiring = mCallback.i;
   1.555 +  mFiring = true;
   1.556 +
   1.557 +  // Handle callbacks that re-init the timer, but avoid leaking.
   1.558 +  // See bug 330128.
   1.559 +  CallbackUnion callback = mCallback;
   1.560 +  unsigned callbackType = mCallbackType;
   1.561 +  if (callbackType == CALLBACK_TYPE_INTERFACE)
   1.562 +    NS_ADDREF(callback.i);
   1.563 +  else if (callbackType == CALLBACK_TYPE_OBSERVER)
   1.564 +    NS_ADDREF(callback.o);
   1.565 +  ReleaseCallback();
   1.566 +
   1.567 +  switch (callbackType) {
   1.568 +    case CALLBACK_TYPE_FUNC:
   1.569 +      callback.c(this, mClosure);
   1.570 +      break;
   1.571 +    case CALLBACK_TYPE_INTERFACE:
   1.572 +      callback.i->Notify(this);
   1.573 +      break;
   1.574 +    case CALLBACK_TYPE_OBSERVER:
   1.575 +      callback.o->Observe(static_cast<nsITimer*>(this),
   1.576 +                          NS_TIMER_CALLBACK_TOPIC,
   1.577 +                          nullptr);
   1.578 +      break;
   1.579 +    default:;
   1.580 +  }
   1.581 +
   1.582 +  // If the callback didn't re-init the timer, and it's not a one-shot timer,
   1.583 +  // restore the callback state.
   1.584 +  if (mCallbackType == CALLBACK_TYPE_UNKNOWN &&
   1.585 +      mType != TYPE_ONE_SHOT && !mCanceled) {
   1.586 +    mCallback = callback;
   1.587 +    mCallbackType = callbackType;
   1.588 +  } else {
   1.589 +    // The timer was a one-shot, or the callback was reinitialized.
   1.590 +    if (callbackType == CALLBACK_TYPE_INTERFACE)
   1.591 +      NS_RELEASE(callback.i);
   1.592 +    else if (callbackType == CALLBACK_TYPE_OBSERVER)
   1.593 +      NS_RELEASE(callback.o);
   1.594 +  }
   1.595 +
   1.596 +  mFiring = false;
   1.597 +  mTimerCallbackWhileFiring = nullptr;
   1.598 +
   1.599 +#ifdef DEBUG_TIMERS
   1.600 +  if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   1.601 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
   1.602 +           ("[this=%p] Took %fms to fire timer callback\n",
   1.603 +            this, (TimeStamp::Now() - now).ToMilliseconds()));
   1.604 +  }
   1.605 +#endif
   1.606 +
   1.607 +  // Reschedule repeating timers, except REPEATING_PRECISE which already did
   1.608 +  // that in PostTimerEvent, but make sure that we aren't armed already (which
   1.609 +  // can happen if the callback reinitialized the timer).
   1.610 +  if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
   1.611 +    if (mType == TYPE_REPEATING_SLACK)
   1.612 +      SetDelayInternal(mDelay); // force mTimeout to be recomputed.  For
   1.613 +                                // REPEATING_PRECISE_CAN_SKIP timers this has
   1.614 +                                // already happened.
   1.615 +    if (gThread)
   1.616 +      gThread->AddTimer(this);
   1.617 +  }
   1.618 +}
   1.619 +
   1.620 +void nsTimerEvent::Init()
   1.621 +{
   1.622 +  sAllocator = new TimerEventAllocator();
   1.623 +}
   1.624 +
   1.625 +void nsTimerEvent::Shutdown()
   1.626 +{
   1.627 +  sCanDeleteAllocator = true;
   1.628 +  DeleteAllocatorIfNeeded();
   1.629 +}
   1.630 +
   1.631 +void nsTimerEvent::DeleteAllocatorIfNeeded()
   1.632 +{
   1.633 +  if (sCanDeleteAllocator && sAllocatorUsers == 0) {
   1.634 +    delete sAllocator;
   1.635 +    sAllocator = nullptr;
   1.636 +  }
   1.637 +}
   1.638 +
   1.639 +NS_IMETHODIMP nsTimerEvent::Run()
   1.640 +{
   1.641 +  if (mGeneration != mTimer->GetGeneration())
   1.642 +    return NS_OK;
   1.643 +
   1.644 +#ifdef DEBUG_TIMERS
   1.645 +  if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   1.646 +    TimeStamp now = TimeStamp::Now();
   1.647 +    PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
   1.648 +           ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
   1.649 +            this, (now - mInitTime).ToMilliseconds()));
   1.650 +  }
   1.651 +#endif
   1.652 +
   1.653 +  mTimer->Fire();
   1.654 +  // Since nsTimerImpl is not thread-safe, we should release |mTimer|
   1.655 +  // here in the target thread to avoid race condition. Otherwise,
   1.656 +  // ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
   1.657 +  // timer thread and result in race condition.
   1.658 +  mTimer = nullptr;
   1.659 +
   1.660 +  return NS_OK;
   1.661 +}
   1.662 +
   1.663 +already_AddRefed<nsTimerImpl>
   1.664 +nsTimerImpl::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
   1.665 +{
   1.666 +  nsRefPtr<nsTimerImpl> timer(aTimerRef);
   1.667 +  if (!timer->mEventTarget) {
   1.668 +    NS_ERROR("Attempt to post timer event to NULL event target");
   1.669 +    return timer.forget();
   1.670 +  }
   1.671 +
   1.672 +  // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
   1.673 +
   1.674 +  // Since TimerThread addref'd 'timer' for us, we don't need to addref here.
   1.675 +  // We will release either in ~nsTimerEvent(), or pass the reference back to
   1.676 +  // the caller. We need to copy the generation number from this timer into the
   1.677 +  // event, so we can avoid firing a timer that was re-initialized after being
   1.678 +  // canceled.
   1.679 +
   1.680 +  // Note: We override operator new for this class, and the override is
   1.681 +  // fallible!
   1.682 +  nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
   1.683 +  if (!event)
   1.684 +    return timer.forget();
   1.685 +
   1.686 +#ifdef DEBUG_TIMERS
   1.687 +  if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   1.688 +    event->mInitTime = TimeStamp::Now();
   1.689 +  }
   1.690 +#endif
   1.691 +
   1.692 +  // If this is a repeating precise timer, we need to calculate the time for
   1.693 +  // the next timer to fire before we make the callback.
   1.694 +  if (timer->IsRepeatingPrecisely()) {
   1.695 +    timer->SetDelayInternal(timer->mDelay);
   1.696 +
   1.697 +    // But only re-arm REPEATING_PRECISE timers.
   1.698 +    if (gThread && timer->mType == TYPE_REPEATING_PRECISE) {
   1.699 +      nsresult rv = gThread->AddTimer(timer);
   1.700 +      if (NS_FAILED(rv)) {
   1.701 +        return timer.forget();
   1.702 +      }
   1.703 +    }
   1.704 +  }
   1.705 +
   1.706 +  nsIEventTarget* target = timer->mEventTarget;
   1.707 +  event->SetTimer(timer.forget());
   1.708 +
   1.709 +  nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
   1.710 +  if (NS_FAILED(rv)) {
   1.711 +    timer = event->ForgetTimer();
   1.712 +    if (gThread) {
   1.713 +      gThread->RemoveTimer(timer);
   1.714 +    }
   1.715 +    return timer.forget();
   1.716 +  }
   1.717 +
   1.718 +  return nullptr;
   1.719 +}
   1.720 +
   1.721 +void nsTimerImpl::SetDelayInternal(uint32_t aDelay)
   1.722 +{
   1.723 +  TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
   1.724 +
   1.725 +  mDelay = aDelay;
   1.726 +
   1.727 +  TimeStamp now = TimeStamp::Now();
   1.728 +  if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
   1.729 +    mTimeout = now;
   1.730 +
   1.731 +  mTimeout += delayInterval;
   1.732 +
   1.733 +#ifdef DEBUG_TIMERS
   1.734 +  if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
   1.735 +    if (mStart.IsNull())
   1.736 +      mStart = now;
   1.737 +    else
   1.738 +      mStart2 = now;
   1.739 +  }
   1.740 +#endif
   1.741 +}
   1.742 +
   1.743 +// NOT FOR PUBLIC CONSUMPTION!
   1.744 +nsresult
   1.745 +NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
   1.746 +            uint32_t aDelay, uint32_t aType)
   1.747 +{
   1.748 +    nsTimerImpl* timer = new nsTimerImpl();
   1.749 +    if (timer == nullptr)
   1.750 +        return NS_ERROR_OUT_OF_MEMORY;
   1.751 +    NS_ADDREF(timer);
   1.752 +
   1.753 +    nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure, 
   1.754 +                                              aDelay, aType);
   1.755 +    if (NS_FAILED(rv)) {
   1.756 +        NS_RELEASE(timer);
   1.757 +        return rv;
   1.758 +    }
   1.759 +
   1.760 +    *aResult = timer;
   1.761 +    return NS_OK;
   1.762 +}

mercurial