1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/threads/TimerThread.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,482 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsTimerImpl.h" 1.10 +#include "TimerThread.h" 1.11 + 1.12 +#include "nsThreadUtils.h" 1.13 +#include "pratom.h" 1.14 + 1.15 +#include "nsIObserverService.h" 1.16 +#include "nsIServiceManager.h" 1.17 +#include "mozilla/Services.h" 1.18 +#include "mozilla/ChaosMode.h" 1.19 +#include "mozilla/ArrayUtils.h" 1.20 + 1.21 +#include <math.h> 1.22 + 1.23 +using namespace mozilla; 1.24 + 1.25 +NS_IMPL_ISUPPORTS(TimerThread, nsIRunnable, nsIObserver) 1.26 + 1.27 +TimerThread::TimerThread() : 1.28 + mInitInProgress(false), 1.29 + mInitialized(false), 1.30 + mMonitor("TimerThread.mMonitor"), 1.31 + mShutdown(false), 1.32 + mWaiting(false), 1.33 + mNotified(false), 1.34 + mSleeping(false) 1.35 +{ 1.36 +} 1.37 + 1.38 +TimerThread::~TimerThread() 1.39 +{ 1.40 + mThread = nullptr; 1.41 + 1.42 + NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread"); 1.43 +} 1.44 + 1.45 +nsresult 1.46 +TimerThread::InitLocks() 1.47 +{ 1.48 + return NS_OK; 1.49 +} 1.50 + 1.51 +namespace { 1.52 + 1.53 +class TimerObserverRunnable : public nsRunnable 1.54 +{ 1.55 +public: 1.56 + TimerObserverRunnable(nsIObserver* observer) 1.57 + : mObserver(observer) 1.58 + { } 1.59 + 1.60 + NS_DECL_NSIRUNNABLE 1.61 + 1.62 +private: 1.63 + nsCOMPtr<nsIObserver> mObserver; 1.64 +}; 1.65 + 1.66 +NS_IMETHODIMP 1.67 +TimerObserverRunnable::Run() 1.68 +{ 1.69 + nsCOMPtr<nsIObserverService> observerService = 1.70 + mozilla::services::GetObserverService(); 1.71 + if (observerService) { 1.72 + observerService->AddObserver(mObserver, "sleep_notification", false); 1.73 + observerService->AddObserver(mObserver, "wake_notification", false); 1.74 + observerService->AddObserver(mObserver, "suspend_process_notification", false); 1.75 + observerService->AddObserver(mObserver, "resume_process_notification", false); 1.76 + } 1.77 + return NS_OK; 1.78 +} 1.79 + 1.80 +} // anonymous namespace 1.81 + 1.82 +nsresult TimerThread::Init() 1.83 +{ 1.84 + PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Init [%d]\n", mInitialized)); 1.85 + 1.86 + if (mInitialized) { 1.87 + if (!mThread) 1.88 + return NS_ERROR_FAILURE; 1.89 + 1.90 + return NS_OK; 1.91 + } 1.92 + 1.93 + if (mInitInProgress.exchange(true) == false) { 1.94 + // We hold on to mThread to keep the thread alive. 1.95 + nsresult rv = NS_NewThread(getter_AddRefs(mThread), this); 1.96 + if (NS_FAILED(rv)) { 1.97 + mThread = nullptr; 1.98 + } 1.99 + else { 1.100 + nsRefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this); 1.101 + if (NS_IsMainThread()) { 1.102 + r->Run(); 1.103 + } 1.104 + else { 1.105 + NS_DispatchToMainThread(r); 1.106 + } 1.107 + } 1.108 + 1.109 + { 1.110 + MonitorAutoLock lock(mMonitor); 1.111 + mInitialized = true; 1.112 + mMonitor.NotifyAll(); 1.113 + } 1.114 + } 1.115 + else { 1.116 + MonitorAutoLock lock(mMonitor); 1.117 + while (!mInitialized) { 1.118 + mMonitor.Wait(); 1.119 + } 1.120 + } 1.121 + 1.122 + if (!mThread) 1.123 + return NS_ERROR_FAILURE; 1.124 + 1.125 + return NS_OK; 1.126 +} 1.127 + 1.128 +nsresult TimerThread::Shutdown() 1.129 +{ 1.130 + PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown begin\n")); 1.131 + 1.132 + if (!mThread) 1.133 + return NS_ERROR_NOT_INITIALIZED; 1.134 + 1.135 + nsTArray<nsTimerImpl*> timers; 1.136 + { // lock scope 1.137 + MonitorAutoLock lock(mMonitor); 1.138 + 1.139 + mShutdown = true; 1.140 + 1.141 + // notify the cond var so that Run() can return 1.142 + if (mWaiting) { 1.143 + mNotified = true; 1.144 + mMonitor.Notify(); 1.145 + } 1.146 + 1.147 + // Need to copy content of mTimers array to a local array 1.148 + // because call to timers' ReleaseCallback() (and release its self) 1.149 + // must not be done under the lock. Destructor of a callback 1.150 + // might potentially call some code reentering the same lock 1.151 + // that leads to unexpected behavior or deadlock. 1.152 + // See bug 422472. 1.153 + timers.AppendElements(mTimers); 1.154 + mTimers.Clear(); 1.155 + } 1.156 + 1.157 + uint32_t timersCount = timers.Length(); 1.158 + for (uint32_t i = 0; i < timersCount; i++) { 1.159 + nsTimerImpl *timer = timers[i]; 1.160 + timer->ReleaseCallback(); 1.161 + ReleaseTimerInternal(timer); 1.162 + } 1.163 + 1.164 + mThread->Shutdown(); // wait for the thread to die 1.165 + 1.166 + PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown end\n")); 1.167 + return NS_OK; 1.168 +} 1.169 + 1.170 +#ifdef MOZ_NUWA_PROCESS 1.171 +#include "ipc/Nuwa.h" 1.172 +#endif 1.173 + 1.174 +/* void Run(); */ 1.175 +NS_IMETHODIMP TimerThread::Run() 1.176 +{ 1.177 + PR_SetCurrentThreadName("Timer"); 1.178 + 1.179 +#ifdef MOZ_NUWA_PROCESS 1.180 + if (IsNuwaProcess()) { 1.181 + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, 1.182 + "NuwaMarkCurrentThread is undefined!"); 1.183 + NuwaMarkCurrentThread(nullptr, nullptr); 1.184 + } 1.185 +#endif 1.186 + 1.187 + MonitorAutoLock lock(mMonitor); 1.188 + 1.189 + // We need to know how many microseconds give a positive PRIntervalTime. This 1.190 + // is platform-dependent, we calculate it at runtime now. 1.191 + // First we find a value such that PR_MicrosecondsToInterval(high) = 1 1.192 + int32_t low = 0, high = 1; 1.193 + while (PR_MicrosecondsToInterval(high) == 0) 1.194 + high <<= 1; 1.195 + // We now have 1.196 + // PR_MicrosecondsToInterval(low) = 0 1.197 + // PR_MicrosecondsToInterval(high) = 1 1.198 + // and we can proceed to find the critical value using binary search 1.199 + while (high-low > 1) { 1.200 + int32_t mid = (high+low) >> 1; 1.201 + if (PR_MicrosecondsToInterval(mid) == 0) 1.202 + low = mid; 1.203 + else 1.204 + high = mid; 1.205 + } 1.206 + 1.207 + // Half of the amount of microseconds needed to get positive PRIntervalTime. 1.208 + // We use this to decide how to round our wait times later 1.209 + int32_t halfMicrosecondsIntervalResolution = high >> 1; 1.210 + bool forceRunNextTimer = false; 1.211 + 1.212 + while (!mShutdown) { 1.213 + // Have to use PRIntervalTime here, since PR_WaitCondVar takes it 1.214 + PRIntervalTime waitFor; 1.215 + bool forceRunThisTimer = forceRunNextTimer; 1.216 + forceRunNextTimer = false; 1.217 + 1.218 + if (mSleeping) { 1.219 + // Sleep for 0.1 seconds while not firing timers. 1.220 + uint32_t milliseconds = 100; 1.221 + if (ChaosMode::isActive()) { 1.222 + milliseconds = ChaosMode::randomUint32LessThan(200); 1.223 + } 1.224 + waitFor = PR_MillisecondsToInterval(milliseconds); 1.225 + } else { 1.226 + waitFor = PR_INTERVAL_NO_TIMEOUT; 1.227 + TimeStamp now = TimeStamp::Now(); 1.228 + nsTimerImpl *timer = nullptr; 1.229 + 1.230 + if (!mTimers.IsEmpty()) { 1.231 + timer = mTimers[0]; 1.232 + 1.233 + if (now >= timer->mTimeout || forceRunThisTimer) { 1.234 + next: 1.235 + // NB: AddRef before the Release under RemoveTimerInternal to avoid 1.236 + // mRefCnt passing through zero, in case all other refs than the one 1.237 + // from mTimers have gone away (the last non-mTimers[i]-ref's Release 1.238 + // must be racing with us, blocked in gThread->RemoveTimer waiting 1.239 + // for TimerThread::mMonitor, under nsTimerImpl::Release. 1.240 + 1.241 + nsRefPtr<nsTimerImpl> timerRef(timer); 1.242 + RemoveTimerInternal(timer); 1.243 + timer = nullptr; 1.244 + 1.245 + { 1.246 + // We release mMonitor around the Fire call to avoid deadlock. 1.247 + MonitorAutoUnlock unlock(mMonitor); 1.248 + 1.249 +#ifdef DEBUG_TIMERS 1.250 + if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) { 1.251 + PR_LOG(GetTimerLog(), PR_LOG_DEBUG, 1.252 + ("Timer thread woke up %fms from when it was supposed to\n", 1.253 + fabs((now - timerRef->mTimeout).ToMilliseconds()))); 1.254 + } 1.255 +#endif 1.256 + 1.257 + // We are going to let the call to PostTimerEvent here handle the 1.258 + // release of the timer so that we don't end up releasing the timer 1.259 + // on the TimerThread instead of on the thread it targets. 1.260 + timerRef = nsTimerImpl::PostTimerEvent(timerRef.forget()); 1.261 + 1.262 + if (timerRef) { 1.263 + // We got our reference back due to an error. 1.264 + // Unhook the nsRefPtr, and release manually so we can get the 1.265 + // refcount. 1.266 + nsrefcnt rc = timerRef.forget().take()->Release(); 1.267 + (void)rc; 1.268 + 1.269 + // The nsITimer interface requires that its users keep a reference 1.270 + // to the timers they use while those timers are initialized but 1.271 + // have not yet fired. If this ever happens, it is a bug in the 1.272 + // code that created and used the timer. 1.273 + // 1.274 + // Further, note that this should never happen even with a 1.275 + // misbehaving user, because nsTimerImpl::Release checks for a 1.276 + // refcount of 1 with an armed timer (a timer whose only reference 1.277 + // is from the timer thread) and when it hits this will remove the 1.278 + // timer from the timer thread and thus destroy the last reference, 1.279 + // preventing this situation from occurring. 1.280 + MOZ_ASSERT(rc != 0, "destroyed timer off its target thread!"); 1.281 + } 1.282 + } 1.283 + 1.284 + if (mShutdown) 1.285 + break; 1.286 + 1.287 + // Update now, as PostTimerEvent plus the locking may have taken a 1.288 + // tick or two, and we may goto next below. 1.289 + now = TimeStamp::Now(); 1.290 + } 1.291 + } 1.292 + 1.293 + if (!mTimers.IsEmpty()) { 1.294 + timer = mTimers[0]; 1.295 + 1.296 + TimeStamp timeout = timer->mTimeout; 1.297 + 1.298 + // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer 1.299 + // is due now or overdue. 1.300 + // 1.301 + // Note that we can only sleep for integer values of a certain 1.302 + // resolution. We use halfMicrosecondsIntervalResolution, calculated 1.303 + // before, to do the optimal rounding (i.e., of how to decide what 1.304 + // interval is so small we should not wait at all). 1.305 + double microseconds = (timeout - now).ToMilliseconds()*1000; 1.306 + 1.307 + if (ChaosMode::isActive()) { 1.308 + // The mean value of sFractions must be 1 to ensure that 1.309 + // the average of a long sequence of timeouts converges to the 1.310 + // actual sum of their times. 1.311 + static const float sFractions[] = { 1.312 + 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f 1.313 + }; 1.314 + microseconds *= sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))]; 1.315 + forceRunNextTimer = true; 1.316 + } 1.317 + 1.318 + if (microseconds < halfMicrosecondsIntervalResolution) { 1.319 + forceRunNextTimer = false; 1.320 + goto next; // round down; execute event now 1.321 + } 1.322 + waitFor = PR_MicrosecondsToInterval(static_cast<uint32_t>(microseconds)); // Floor is accurate enough. 1.323 + if (waitFor == 0) 1.324 + waitFor = 1; // round up, wait the minimum time we can wait 1.325 + } 1.326 + 1.327 +#ifdef DEBUG_TIMERS 1.328 + if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) { 1.329 + if (waitFor == PR_INTERVAL_NO_TIMEOUT) 1.330 + PR_LOG(GetTimerLog(), PR_LOG_DEBUG, 1.331 + ("waiting for PR_INTERVAL_NO_TIMEOUT\n")); 1.332 + else 1.333 + PR_LOG(GetTimerLog(), PR_LOG_DEBUG, 1.334 + ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor))); 1.335 + } 1.336 +#endif 1.337 + } 1.338 + 1.339 + mWaiting = true; 1.340 + mNotified = false; 1.341 + mMonitor.Wait(waitFor); 1.342 + if (mNotified) { 1.343 + forceRunNextTimer = false; 1.344 + } 1.345 + mWaiting = false; 1.346 + } 1.347 + 1.348 + return NS_OK; 1.349 +} 1.350 + 1.351 +nsresult TimerThread::AddTimer(nsTimerImpl *aTimer) 1.352 +{ 1.353 + MonitorAutoLock lock(mMonitor); 1.354 + 1.355 + // Add the timer to our list. 1.356 + int32_t i = AddTimerInternal(aTimer); 1.357 + if (i < 0) 1.358 + return NS_ERROR_OUT_OF_MEMORY; 1.359 + 1.360 + // Awaken the timer thread. 1.361 + if (mWaiting && i == 0) { 1.362 + mNotified = true; 1.363 + mMonitor.Notify(); 1.364 + } 1.365 + 1.366 + return NS_OK; 1.367 +} 1.368 + 1.369 +nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer) 1.370 +{ 1.371 + MonitorAutoLock lock(mMonitor); 1.372 + 1.373 + // Our caller has a strong ref to aTimer, so it can't go away here under 1.374 + // ReleaseTimerInternal. 1.375 + RemoveTimerInternal(aTimer); 1.376 + 1.377 + int32_t i = AddTimerInternal(aTimer); 1.378 + if (i < 0) 1.379 + return NS_ERROR_OUT_OF_MEMORY; 1.380 + 1.381 + // Awaken the timer thread. 1.382 + if (mWaiting && i == 0) { 1.383 + mNotified = true; 1.384 + mMonitor.Notify(); 1.385 + } 1.386 + 1.387 + return NS_OK; 1.388 +} 1.389 + 1.390 +nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer) 1.391 +{ 1.392 + MonitorAutoLock lock(mMonitor); 1.393 + 1.394 + // Remove the timer from our array. Tell callers that aTimer was not found 1.395 + // by returning NS_ERROR_NOT_AVAILABLE. Unlike the TimerDelayChanged case 1.396 + // immediately above, our caller may be passing a (now-)weak ref in via the 1.397 + // aTimer param, specifically when nsTimerImpl::Release loses a race with 1.398 + // TimerThread::Run, must wait for the mMonitor auto-lock here, and during the 1.399 + // wait Run drops the only remaining ref to aTimer via RemoveTimerInternal. 1.400 + 1.401 + if (!RemoveTimerInternal(aTimer)) 1.402 + return NS_ERROR_NOT_AVAILABLE; 1.403 + 1.404 + // Awaken the timer thread. 1.405 + if (mWaiting) { 1.406 + mNotified = true; 1.407 + mMonitor.Notify(); 1.408 + } 1.409 + 1.410 + return NS_OK; 1.411 +} 1.412 + 1.413 +// This function must be called from within a lock 1.414 +int32_t TimerThread::AddTimerInternal(nsTimerImpl *aTimer) 1.415 +{ 1.416 + if (mShutdown) 1.417 + return -1; 1.418 + 1.419 + TimeStamp now = TimeStamp::Now(); 1.420 + 1.421 + TimerAdditionComparator c(now, aTimer); 1.422 + nsTimerImpl** insertSlot = mTimers.InsertElementSorted(aTimer, c); 1.423 + 1.424 + if (!insertSlot) 1.425 + return -1; 1.426 + 1.427 + aTimer->mArmed = true; 1.428 + NS_ADDREF(aTimer); 1.429 + 1.430 +#ifdef MOZ_TASK_TRACER 1.431 + aTimer->DispatchTracedTask(); 1.432 +#endif 1.433 + 1.434 + return insertSlot - mTimers.Elements(); 1.435 +} 1.436 + 1.437 +bool TimerThread::RemoveTimerInternal(nsTimerImpl *aTimer) 1.438 +{ 1.439 + if (!mTimers.RemoveElement(aTimer)) 1.440 + return false; 1.441 + 1.442 + ReleaseTimerInternal(aTimer); 1.443 + return true; 1.444 +} 1.445 + 1.446 +void TimerThread::ReleaseTimerInternal(nsTimerImpl *aTimer) 1.447 +{ 1.448 + // Order is crucial here -- see nsTimerImpl::Release. 1.449 + aTimer->mArmed = false; 1.450 + NS_RELEASE(aTimer); 1.451 +} 1.452 + 1.453 +void TimerThread::DoBeforeSleep() 1.454 +{ 1.455 + mSleeping = true; 1.456 +} 1.457 + 1.458 +void TimerThread::DoAfterSleep() 1.459 +{ 1.460 + mSleeping = true; // wake may be notified without preceding sleep notification 1.461 + for (uint32_t i = 0; i < mTimers.Length(); i ++) { 1.462 + nsTimerImpl *timer = mTimers[i]; 1.463 + // get and set the delay to cause its timeout to be recomputed 1.464 + uint32_t delay; 1.465 + timer->GetDelay(&delay); 1.466 + timer->SetDelay(delay); 1.467 + } 1.468 + 1.469 + mSleeping = false; 1.470 +} 1.471 + 1.472 + 1.473 +/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */ 1.474 +NS_IMETHODIMP 1.475 +TimerThread::Observe(nsISupports* /* aSubject */, const char *aTopic, const char16_t* /* aData */) 1.476 +{ 1.477 + if (strcmp(aTopic, "sleep_notification") == 0 || 1.478 + strcmp(aTopic, "suspend_process_notification") == 0) 1.479 + DoBeforeSleep(); 1.480 + else if (strcmp(aTopic, "wake_notification") == 0 || 1.481 + strcmp(aTopic, "resume_process_notification") == 0) 1.482 + DoAfterSleep(); 1.483 + 1.484 + return NS_OK; 1.485 +}