Bumping manifests a=b2g-bump
[gecko.git] / xpcom / threads / TimerThread.cpp
blob7d46f0c5efa16df44fe8c89171475474689a2559
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsTimerImpl.h"
8 #include "TimerThread.h"
10 #include "nsThreadUtils.h"
11 #include "pratom.h"
13 #include "nsIObserverService.h"
14 #include "nsIServiceManager.h"
15 #include "mozilla/Services.h"
16 #include "mozilla/ChaosMode.h"
17 #include "mozilla/ArrayUtils.h"
18 #include "mozilla/BinarySearch.h"
20 #include <math.h>
22 using namespace mozilla;
24 NS_IMPL_ISUPPORTS(TimerThread, nsIRunnable, nsIObserver)
26 TimerThread::TimerThread() :
27 mInitInProgress(false),
28 mInitialized(false),
29 mMonitor("TimerThread.mMonitor"),
30 mShutdown(false),
31 mWaiting(false),
32 mNotified(false),
33 mSleeping(false),
34 mLastTimerEventLoopRun(TimeStamp::Now())
38 TimerThread::~TimerThread()
40 mThread = nullptr;
42 NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
45 nsresult
46 TimerThread::InitLocks()
48 return NS_OK;
51 namespace {
53 class TimerObserverRunnable : public nsRunnable
55 public:
56 explicit TimerObserverRunnable(nsIObserver* aObserver)
57 : mObserver(aObserver)
61 NS_DECL_NSIRUNNABLE
63 private:
64 nsCOMPtr<nsIObserver> mObserver;
67 NS_IMETHODIMP
68 TimerObserverRunnable::Run()
70 nsCOMPtr<nsIObserverService> observerService =
71 mozilla::services::GetObserverService();
72 if (observerService) {
73 observerService->AddObserver(mObserver, "sleep_notification", false);
74 observerService->AddObserver(mObserver, "wake_notification", false);
75 observerService->AddObserver(mObserver, "suspend_process_notification", false);
76 observerService->AddObserver(mObserver, "resume_process_notification", false);
78 return NS_OK;
81 } // anonymous namespace
83 nsresult
84 TimerThread::Init()
86 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
87 ("TimerThread::Init [%d]\n", mInitialized));
89 if (mInitialized) {
90 if (!mThread) {
91 return NS_ERROR_FAILURE;
94 return NS_OK;
97 if (mInitInProgress.exchange(true) == false) {
98 // We hold on to mThread to keep the thread alive.
99 nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
100 if (NS_FAILED(rv)) {
101 mThread = nullptr;
102 } else {
103 nsRefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
104 if (NS_IsMainThread()) {
105 r->Run();
106 } else {
107 NS_DispatchToMainThread(r);
112 MonitorAutoLock lock(mMonitor);
113 mInitialized = true;
114 mMonitor.NotifyAll();
116 } else {
117 MonitorAutoLock lock(mMonitor);
118 while (!mInitialized) {
119 mMonitor.Wait();
123 if (!mThread) {
124 return NS_ERROR_FAILURE;
127 return NS_OK;
130 nsresult
131 TimerThread::Shutdown()
133 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown begin\n"));
135 if (!mThread) {
136 return NS_ERROR_NOT_INITIALIZED;
139 nsTArray<nsTimerImpl*> timers;
141 // lock scope
142 MonitorAutoLock lock(mMonitor);
144 mShutdown = true;
146 // notify the cond var so that Run() can return
147 if (mWaiting) {
148 mNotified = true;
149 mMonitor.Notify();
152 // Need to copy content of mTimers array to a local array
153 // because call to timers' ReleaseCallback() (and release its self)
154 // must not be done under the lock. Destructor of a callback
155 // might potentially call some code reentering the same lock
156 // that leads to unexpected behavior or deadlock.
157 // See bug 422472.
158 timers.AppendElements(mTimers);
159 mTimers.Clear();
162 uint32_t timersCount = timers.Length();
163 for (uint32_t i = 0; i < timersCount; i++) {
164 nsTimerImpl* timer = timers[i];
165 timer->ReleaseCallback();
166 ReleaseTimerInternal(timer);
169 mThread->Shutdown(); // wait for the thread to die
171 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown end\n"));
172 return NS_OK;
175 #ifdef MOZ_NUWA_PROCESS
176 #include "ipc/Nuwa.h"
177 #endif
179 namespace {
181 struct MicrosecondsToInterval
183 PRIntervalTime operator[](size_t aMs) const {
184 return PR_MicrosecondsToInterval(aMs);
188 struct IntervalComparator
190 int operator()(PRIntervalTime aInterval) const {
191 return (0 < aInterval) ? -1 : 1;
195 } // namespace
197 /* void Run(); */
198 NS_IMETHODIMP
199 TimerThread::Run()
201 PR_SetCurrentThreadName("Timer");
203 #ifdef MOZ_NUWA_PROCESS
204 if (IsNuwaProcess()) {
205 NuwaMarkCurrentThread(nullptr, nullptr);
207 #endif
209 MonitorAutoLock lock(mMonitor);
211 // We need to know how many microseconds give a positive PRIntervalTime. This
212 // is platform-dependent and we calculate it at runtime, finding a value |v|
213 // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
214 // the range [0, v) to find the ms-to-interval scale.
215 uint32_t usForPosInterval = 1;
216 while (PR_MicrosecondsToInterval(usForPosInterval) == 0) {
217 usForPosInterval <<= 1;
220 size_t usIntervalResolution;
221 BinarySearchIf(MicrosecondsToInterval(), 0, usForPosInterval, IntervalComparator(), &usIntervalResolution);
222 MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution - 1) == 0);
223 MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution) == 1);
225 // Half of the amount of microseconds needed to get positive PRIntervalTime.
226 // We use this to decide how to round our wait times later
227 int32_t halfMicrosecondsIntervalResolution = usIntervalResolution / 2;
228 bool forceRunNextTimer = false;
230 while (!mShutdown) {
231 // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
232 PRIntervalTime waitFor;
233 bool forceRunThisTimer = forceRunNextTimer;
234 forceRunNextTimer = false;
236 if (mSleeping) {
237 // Sleep for 0.1 seconds while not firing timers.
238 uint32_t milliseconds = 100;
239 if (ChaosMode::isActive(ChaosMode::TimerScheduling)) {
240 milliseconds = ChaosMode::randomUint32LessThan(200);
242 waitFor = PR_MillisecondsToInterval(milliseconds);
243 } else {
244 waitFor = PR_INTERVAL_NO_TIMEOUT;
245 TimeStamp now = TimeStamp::Now();
246 mLastTimerEventLoopRun = now;
247 nsTimerImpl* timer = nullptr;
249 if (!mTimers.IsEmpty()) {
250 timer = mTimers[0];
252 if (now >= timer->mTimeout || forceRunThisTimer) {
253 next:
254 // NB: AddRef before the Release under RemoveTimerInternal to avoid
255 // mRefCnt passing through zero, in case all other refs than the one
256 // from mTimers have gone away (the last non-mTimers[i]-ref's Release
257 // must be racing with us, blocked in gThread->RemoveTimer waiting
258 // for TimerThread::mMonitor, under nsTimerImpl::Release.
260 nsRefPtr<nsTimerImpl> timerRef(timer);
261 RemoveTimerInternal(timer);
262 timer = nullptr;
264 #ifdef DEBUG_TIMERS
265 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
266 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
267 ("Timer thread woke up %fms from when it was supposed to\n",
268 fabs((now - timerRef->mTimeout).ToMilliseconds())));
270 #endif
273 // We release mMonitor around the Fire call to avoid deadlock.
274 MonitorAutoUnlock unlock(mMonitor);
276 // We are going to let the call to PostTimerEvent here handle the
277 // release of the timer so that we don't end up releasing the timer
278 // on the TimerThread instead of on the thread it targets.
279 timerRef = nsTimerImpl::PostTimerEvent(timerRef.forget());
282 if (timerRef) {
283 // We got our reference back due to an error.
284 // Unhook the nsRefPtr, and release manually so we can get the
285 // refcount.
286 nsrefcnt rc = timerRef.forget().take()->Release();
287 (void)rc;
289 // The nsITimer interface requires that its users keep a reference
290 // to the timers they use while those timers are initialized but
291 // have not yet fired. If this ever happens, it is a bug in the
292 // code that created and used the timer.
294 // Further, note that this should never happen even with a
295 // misbehaving user, because nsTimerImpl::Release checks for a
296 // refcount of 1 with an armed timer (a timer whose only reference
297 // is from the timer thread) and when it hits this will remove the
298 // timer from the timer thread and thus destroy the last reference,
299 // preventing this situation from occurring.
300 MOZ_ASSERT(rc != 0, "destroyed timer off its target thread!");
303 if (mShutdown) {
304 break;
307 // Update now, as PostTimerEvent plus the locking may have taken a
308 // tick or two, and we may goto next below.
309 now = TimeStamp::Now();
313 if (!mTimers.IsEmpty()) {
314 timer = mTimers[0];
316 TimeStamp timeout = timer->mTimeout;
318 // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
319 // is due now or overdue.
321 // Note that we can only sleep for integer values of a certain
322 // resolution. We use halfMicrosecondsIntervalResolution, calculated
323 // before, to do the optimal rounding (i.e., of how to decide what
324 // interval is so small we should not wait at all).
325 double microseconds = (timeout - now).ToMilliseconds() * 1000;
327 if (ChaosMode::isActive(ChaosMode::TimerScheduling)) {
328 // The mean value of sFractions must be 1 to ensure that
329 // the average of a long sequence of timeouts converges to the
330 // actual sum of their times.
331 static const float sFractions[] = {
332 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
334 microseconds *=
335 sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
336 forceRunNextTimer = true;
339 if (microseconds < halfMicrosecondsIntervalResolution) {
340 forceRunNextTimer = false;
341 goto next; // round down; execute event now
343 waitFor = PR_MicrosecondsToInterval(
344 static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
345 if (waitFor == 0) {
346 waitFor = 1; // round up, wait the minimum time we can wait
350 #ifdef DEBUG_TIMERS
351 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
352 if (waitFor == PR_INTERVAL_NO_TIMEOUT)
353 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
354 ("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
355 else
356 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
357 ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
359 #endif
362 mWaiting = true;
363 mNotified = false;
364 mMonitor.Wait(waitFor);
365 if (mNotified) {
366 forceRunNextTimer = false;
368 mWaiting = false;
371 return NS_OK;
374 nsresult
375 TimerThread::AddTimer(nsTimerImpl* aTimer)
377 MonitorAutoLock lock(mMonitor);
379 // Add the timer to our list.
380 int32_t i = AddTimerInternal(aTimer);
381 if (i < 0) {
382 return NS_ERROR_OUT_OF_MEMORY;
385 // Awaken the timer thread.
386 if (mWaiting && i == 0) {
387 mNotified = true;
388 mMonitor.Notify();
391 return NS_OK;
394 nsresult
395 TimerThread::TimerDelayChanged(nsTimerImpl* aTimer)
397 MonitorAutoLock lock(mMonitor);
399 // Our caller has a strong ref to aTimer, so it can't go away here under
400 // ReleaseTimerInternal.
401 RemoveTimerInternal(aTimer);
403 int32_t i = AddTimerInternal(aTimer);
404 if (i < 0) {
405 return NS_ERROR_OUT_OF_MEMORY;
408 // Awaken the timer thread.
409 if (mWaiting && i == 0) {
410 mNotified = true;
411 mMonitor.Notify();
414 return NS_OK;
417 nsresult
418 TimerThread::RemoveTimer(nsTimerImpl* aTimer)
420 MonitorAutoLock lock(mMonitor);
422 // Remove the timer from our array. Tell callers that aTimer was not found
423 // by returning NS_ERROR_NOT_AVAILABLE. Unlike the TimerDelayChanged case
424 // immediately above, our caller may be passing a (now-)weak ref in via the
425 // aTimer param, specifically when nsTimerImpl::Release loses a race with
426 // TimerThread::Run, must wait for the mMonitor auto-lock here, and during the
427 // wait Run drops the only remaining ref to aTimer via RemoveTimerInternal.
429 if (!RemoveTimerInternal(aTimer)) {
430 return NS_ERROR_NOT_AVAILABLE;
433 // Awaken the timer thread.
434 if (mWaiting) {
435 mNotified = true;
436 mMonitor.Notify();
439 return NS_OK;
442 // This function must be called from within a lock
443 int32_t
444 TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
446 mMonitor.AssertCurrentThreadOwns();
447 if (mShutdown) {
448 return -1;
451 TimeStamp now = TimeStamp::Now();
453 TimerAdditionComparator c(now, aTimer);
454 nsTimerImpl** insertSlot = mTimers.InsertElementSorted(aTimer, c);
456 if (!insertSlot) {
457 return -1;
460 aTimer->mArmed = true;
461 NS_ADDREF(aTimer);
463 #ifdef MOZ_TASK_TRACER
464 // Create a FakeTracedTask, and dispatch it here. This is the start point of
465 // the latency.
466 aTimer->DispatchTracedTask();
467 #endif
469 return insertSlot - mTimers.Elements();
472 bool
473 TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
475 mMonitor.AssertCurrentThreadOwns();
476 if (!mTimers.RemoveElement(aTimer)) {
477 return false;
480 ReleaseTimerInternal(aTimer);
481 return true;
484 void
485 TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
487 if (!mShutdown) {
488 // copied to a local array before releasing in shutdown
489 mMonitor.AssertCurrentThreadOwns();
491 // Order is crucial here -- see nsTimerImpl::Release.
492 aTimer->mArmed = false;
493 NS_RELEASE(aTimer);
496 void
497 TimerThread::DoBeforeSleep()
499 // Mainthread
500 MonitorAutoLock lock(mMonitor);
501 mLastTimerEventLoopRun = TimeStamp::Now();
502 mSleeping = true;
505 // Note: wake may be notified without preceding sleep notification
506 void
507 TimerThread::DoAfterSleep()
509 // Mainthread
510 TimeStamp now = TimeStamp::Now();
512 MonitorAutoLock lock(mMonitor);
514 // an over-estimate of time slept, usually small
515 TimeDuration slept = now - mLastTimerEventLoopRun;
517 // Adjust all old timers to expire roughly similar times in the future
518 // compared to when we went to sleep, by adding the time we slept to the
519 // target time. It's slightly possible a few will end up slightly in the
520 // past and fire immediately, but ordering should be preserved. All
521 // timers retain the exact same order (and relative times) as before
522 // going to sleep.
523 for (uint32_t i = 0; i < mTimers.Length(); i ++) {
524 nsTimerImpl* timer = mTimers[i];
525 timer->mTimeout += slept;
527 mSleeping = false;
528 mLastTimerEventLoopRun = now;
530 // Wake up the timer thread to process the updated array
531 mNotified = true;
532 mMonitor.Notify();
536 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
537 NS_IMETHODIMP
538 TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
539 const char16_t* /* aData */)
541 if (strcmp(aTopic, "sleep_notification") == 0 ||
542 strcmp(aTopic, "suspend_process_notification") == 0) {
543 DoBeforeSleep();
544 } else if (strcmp(aTopic, "wake_notification") == 0 ||
545 strcmp(aTopic, "resume_process_notification") == 0) {
546 DoAfterSleep();
549 return NS_OK;