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 +}