xpcom/threads/nsTimerImpl.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial