Bumping manifests a=b2g-bump
[gecko.git] / xpcom / threads / TimerThread.cpp
blob8d892342915a582986882b29e9b0335cb4572802
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)
37 TimerThread::~TimerThread()
39 mThread = nullptr;
41 NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
44 nsresult
45 TimerThread::InitLocks()
47 return NS_OK;
50 namespace {
52 class TimerObserverRunnable : public nsRunnable
54 public:
55 explicit TimerObserverRunnable(nsIObserver* aObserver)
56 : mObserver(aObserver)
60 NS_DECL_NSIRUNNABLE
62 private:
63 nsCOMPtr<nsIObserver> mObserver;
66 NS_IMETHODIMP
67 TimerObserverRunnable::Run()
69 nsCOMPtr<nsIObserverService> observerService =
70 mozilla::services::GetObserverService();
71 if (observerService) {
72 observerService->AddObserver(mObserver, "sleep_notification", false);
73 observerService->AddObserver(mObserver, "wake_notification", false);
74 observerService->AddObserver(mObserver, "suspend_process_notification", false);
75 observerService->AddObserver(mObserver, "resume_process_notification", false);
77 return NS_OK;
80 } // anonymous namespace
82 nsresult
83 TimerThread::Init()
85 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
86 ("TimerThread::Init [%d]\n", mInitialized));
88 if (mInitialized) {
89 if (!mThread) {
90 return NS_ERROR_FAILURE;
93 return NS_OK;
96 if (mInitInProgress.exchange(true) == false) {
97 // We hold on to mThread to keep the thread alive.
98 nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
99 if (NS_FAILED(rv)) {
100 mThread = nullptr;
101 } else {
102 nsRefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
103 if (NS_IsMainThread()) {
104 r->Run();
105 } else {
106 NS_DispatchToMainThread(r);
111 MonitorAutoLock lock(mMonitor);
112 mInitialized = true;
113 mMonitor.NotifyAll();
115 } else {
116 MonitorAutoLock lock(mMonitor);
117 while (!mInitialized) {
118 mMonitor.Wait();
122 if (!mThread) {
123 return NS_ERROR_FAILURE;
126 return NS_OK;
129 nsresult
130 TimerThread::Shutdown()
132 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown begin\n"));
134 if (!mThread) {
135 return NS_ERROR_NOT_INITIALIZED;
138 nsTArray<nsTimerImpl*> timers;
140 // lock scope
141 MonitorAutoLock lock(mMonitor);
143 mShutdown = true;
145 // notify the cond var so that Run() can return
146 if (mWaiting) {
147 mNotified = true;
148 mMonitor.Notify();
151 // Need to copy content of mTimers array to a local array
152 // because call to timers' ReleaseCallback() (and release its self)
153 // must not be done under the lock. Destructor of a callback
154 // might potentially call some code reentering the same lock
155 // that leads to unexpected behavior or deadlock.
156 // See bug 422472.
157 timers.AppendElements(mTimers);
158 mTimers.Clear();
161 uint32_t timersCount = timers.Length();
162 for (uint32_t i = 0; i < timersCount; i++) {
163 nsTimerImpl* timer = timers[i];
164 timer->ReleaseCallback();
165 ReleaseTimerInternal(timer);
168 mThread->Shutdown(); // wait for the thread to die
170 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown end\n"));
171 return NS_OK;
174 #ifdef MOZ_NUWA_PROCESS
175 #include "ipc/Nuwa.h"
176 #endif
178 namespace {
180 struct MicrosecondsToInterval
182 PRIntervalTime operator[](size_t aMs) const {
183 return PR_MicrosecondsToInterval(aMs);
187 struct IntervalComparator
189 int operator()(PRIntervalTime aInterval) const {
190 return (0 < aInterval) ? -1 : 1;
194 } // namespace
196 /* void Run(); */
197 NS_IMETHODIMP
198 TimerThread::Run()
200 PR_SetCurrentThreadName("Timer");
202 #ifdef MOZ_NUWA_PROCESS
203 if (IsNuwaProcess()) {
204 NuwaMarkCurrentThread(nullptr, nullptr);
206 #endif
208 NS_SetIgnoreStatusOfCurrentThread();
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 nsTimerImpl* timer = nullptr;
248 if (!mTimers.IsEmpty()) {
249 timer = mTimers[0];
251 if (now >= timer->mTimeout || forceRunThisTimer) {
252 next:
253 // NB: AddRef before the Release under RemoveTimerInternal to avoid
254 // mRefCnt passing through zero, in case all other refs than the one
255 // from mTimers have gone away (the last non-mTimers[i]-ref's Release
256 // must be racing with us, blocked in gThread->RemoveTimer waiting
257 // for TimerThread::mMonitor, under nsTimerImpl::Release.
259 nsRefPtr<nsTimerImpl> timerRef(timer);
260 RemoveTimerInternal(timer);
261 timer = nullptr;
263 #ifdef DEBUG_TIMERS
264 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
265 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
266 ("Timer thread woke up %fms from when it was supposed to\n",
267 fabs((now - timerRef->mTimeout).ToMilliseconds())));
269 #endif
272 // We release mMonitor around the Fire call to avoid deadlock.
273 MonitorAutoUnlock unlock(mMonitor);
275 // We are going to let the call to PostTimerEvent here handle the
276 // release of the timer so that we don't end up releasing the timer
277 // on the TimerThread instead of on the thread it targets.
278 timerRef = nsTimerImpl::PostTimerEvent(timerRef.forget());
281 if (timerRef) {
282 // We got our reference back due to an error.
283 // Unhook the nsRefPtr, and release manually so we can get the
284 // refcount.
285 nsrefcnt rc = timerRef.forget().take()->Release();
286 (void)rc;
288 // The nsITimer interface requires that its users keep a reference
289 // to the timers they use while those timers are initialized but
290 // have not yet fired. If this ever happens, it is a bug in the
291 // code that created and used the timer.
293 // Further, note that this should never happen even with a
294 // misbehaving user, because nsTimerImpl::Release checks for a
295 // refcount of 1 with an armed timer (a timer whose only reference
296 // is from the timer thread) and when it hits this will remove the
297 // timer from the timer thread and thus destroy the last reference,
298 // preventing this situation from occurring.
299 MOZ_ASSERT(rc != 0, "destroyed timer off its target thread!");
302 if (mShutdown) {
303 break;
306 // Update now, as PostTimerEvent plus the locking may have taken a
307 // tick or two, and we may goto next below.
308 now = TimeStamp::Now();
312 if (!mTimers.IsEmpty()) {
313 timer = mTimers[0];
315 TimeStamp timeout = timer->mTimeout;
317 // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
318 // is due now or overdue.
320 // Note that we can only sleep for integer values of a certain
321 // resolution. We use halfMicrosecondsIntervalResolution, calculated
322 // before, to do the optimal rounding (i.e., of how to decide what
323 // interval is so small we should not wait at all).
324 double microseconds = (timeout - now).ToMilliseconds() * 1000;
326 if (ChaosMode::isActive(ChaosMode::TimerScheduling)) {
327 // The mean value of sFractions must be 1 to ensure that
328 // the average of a long sequence of timeouts converges to the
329 // actual sum of their times.
330 static const float sFractions[] = {
331 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
333 microseconds *=
334 sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
335 forceRunNextTimer = true;
338 if (microseconds < halfMicrosecondsIntervalResolution) {
339 forceRunNextTimer = false;
340 goto next; // round down; execute event now
342 waitFor = PR_MicrosecondsToInterval(
343 static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
344 if (waitFor == 0) {
345 waitFor = 1; // round up, wait the minimum time we can wait
349 #ifdef DEBUG_TIMERS
350 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
351 if (waitFor == PR_INTERVAL_NO_TIMEOUT)
352 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
353 ("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
354 else
355 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
356 ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
358 #endif
361 mWaiting = true;
362 mNotified = false;
363 mMonitor.Wait(waitFor);
364 if (mNotified) {
365 forceRunNextTimer = false;
367 mWaiting = false;
370 return NS_OK;
373 nsresult
374 TimerThread::AddTimer(nsTimerImpl* aTimer)
376 MonitorAutoLock lock(mMonitor);
378 // Add the timer to our list.
379 int32_t i = AddTimerInternal(aTimer);
380 if (i < 0) {
381 return NS_ERROR_OUT_OF_MEMORY;
384 // Awaken the timer thread.
385 if (mWaiting && i == 0) {
386 mNotified = true;
387 mMonitor.Notify();
390 return NS_OK;
393 nsresult
394 TimerThread::TimerDelayChanged(nsTimerImpl* aTimer)
396 MonitorAutoLock lock(mMonitor);
398 // Our caller has a strong ref to aTimer, so it can't go away here under
399 // ReleaseTimerInternal.
400 RemoveTimerInternal(aTimer);
402 int32_t i = AddTimerInternal(aTimer);
403 if (i < 0) {
404 return NS_ERROR_OUT_OF_MEMORY;
407 // Awaken the timer thread.
408 if (mWaiting && i == 0) {
409 mNotified = true;
410 mMonitor.Notify();
413 return NS_OK;
416 nsresult
417 TimerThread::RemoveTimer(nsTimerImpl* aTimer)
419 MonitorAutoLock lock(mMonitor);
421 // Remove the timer from our array. Tell callers that aTimer was not found
422 // by returning NS_ERROR_NOT_AVAILABLE. Unlike the TimerDelayChanged case
423 // immediately above, our caller may be passing a (now-)weak ref in via the
424 // aTimer param, specifically when nsTimerImpl::Release loses a race with
425 // TimerThread::Run, must wait for the mMonitor auto-lock here, and during the
426 // wait Run drops the only remaining ref to aTimer via RemoveTimerInternal.
428 if (!RemoveTimerInternal(aTimer)) {
429 return NS_ERROR_NOT_AVAILABLE;
432 // Awaken the timer thread.
433 if (mWaiting) {
434 mNotified = true;
435 mMonitor.Notify();
438 return NS_OK;
441 // This function must be called from within a lock
442 int32_t
443 TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
445 if (mShutdown) {
446 return -1;
449 TimeStamp now = TimeStamp::Now();
451 TimerAdditionComparator c(now, aTimer);
452 nsTimerImpl** insertSlot = mTimers.InsertElementSorted(aTimer, c);
454 if (!insertSlot) {
455 return -1;
458 aTimer->mArmed = true;
459 NS_ADDREF(aTimer);
461 #ifdef MOZ_TASK_TRACER
462 // Create a FakeTracedTask, and dispatch it here. This is the start point of
463 // the latency.
464 aTimer->DispatchTracedTask();
465 #endif
467 return insertSlot - mTimers.Elements();
470 bool
471 TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
473 if (!mTimers.RemoveElement(aTimer)) {
474 return false;
477 ReleaseTimerInternal(aTimer);
478 return true;
481 void
482 TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
484 // Order is crucial here -- see nsTimerImpl::Release.
485 aTimer->mArmed = false;
486 NS_RELEASE(aTimer);
489 void
490 TimerThread::DoBeforeSleep()
492 mSleeping = true;
495 void
496 TimerThread::DoAfterSleep()
498 mSleeping = true; // wake may be notified without preceding sleep notification
499 for (uint32_t i = 0; i < mTimers.Length(); i ++) {
500 nsTimerImpl* timer = mTimers[i];
501 // get and set the delay to cause its timeout to be recomputed
502 uint32_t delay;
503 timer->GetDelay(&delay);
504 timer->SetDelay(delay);
507 mSleeping = false;
511 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
512 NS_IMETHODIMP
513 TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
514 const char16_t* /* aData */)
516 if (strcmp(aTopic, "sleep_notification") == 0 ||
517 strcmp(aTopic, "suspend_process_notification") == 0) {
518 DoBeforeSleep();
519 } else if (strcmp(aTopic, "wake_notification") == 0 ||
520 strcmp(aTopic, "resume_process_notification") == 0) {
521 DoAfterSleep();
524 return NS_OK;