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"
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"
22 using namespace mozilla
;
24 NS_IMPL_ISUPPORTS(TimerThread
, nsIRunnable
, nsIObserver
)
26 TimerThread::TimerThread() :
27 mInitInProgress(false),
29 mMonitor("TimerThread.mMonitor"),
37 TimerThread::~TimerThread()
41 NS_ASSERTION(mTimers
.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
45 TimerThread::InitLocks()
52 class TimerObserverRunnable
: public nsRunnable
55 explicit TimerObserverRunnable(nsIObserver
* aObserver
)
56 : mObserver(aObserver
)
63 nsCOMPtr
<nsIObserver
> mObserver
;
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);
80 } // anonymous namespace
85 PR_LOG(GetTimerLog(), PR_LOG_DEBUG
,
86 ("TimerThread::Init [%d]\n", mInitialized
));
90 return NS_ERROR_FAILURE
;
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);
102 nsRefPtr
<TimerObserverRunnable
> r
= new TimerObserverRunnable(this);
103 if (NS_IsMainThread()) {
106 NS_DispatchToMainThread(r
);
111 MonitorAutoLock
lock(mMonitor
);
113 mMonitor
.NotifyAll();
116 MonitorAutoLock
lock(mMonitor
);
117 while (!mInitialized
) {
123 return NS_ERROR_FAILURE
;
130 TimerThread::Shutdown()
132 PR_LOG(GetTimerLog(), PR_LOG_DEBUG
, ("TimerThread::Shutdown begin\n"));
135 return NS_ERROR_NOT_INITIALIZED
;
138 nsTArray
<nsTimerImpl
*> timers
;
141 MonitorAutoLock
lock(mMonitor
);
145 // notify the cond var so that Run() can return
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.
157 timers
.AppendElements(mTimers
);
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"));
174 #ifdef MOZ_NUWA_PROCESS
175 #include "ipc/Nuwa.h"
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;
200 PR_SetCurrentThreadName("Timer");
202 #ifdef MOZ_NUWA_PROCESS
203 if (IsNuwaProcess()) {
204 NuwaMarkCurrentThread(nullptr, nullptr);
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;
231 // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
232 PRIntervalTime waitFor
;
233 bool forceRunThisTimer
= forceRunNextTimer
;
234 forceRunNextTimer
= false;
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
);
244 waitFor
= PR_INTERVAL_NO_TIMEOUT
;
245 TimeStamp now
= TimeStamp::Now();
246 nsTimerImpl
* timer
= nullptr;
248 if (!mTimers
.IsEmpty()) {
251 if (now
>= timer
->mTimeout
|| forceRunThisTimer
) {
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
);
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())));
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());
282 // We got our reference back due to an error.
283 // Unhook the nsRefPtr, and release manually so we can get the
285 nsrefcnt rc
= timerRef
.forget().take()->Release();
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!");
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()) {
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
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.
345 waitFor
= 1; // round up, wait the minimum time we can wait
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"));
355 PR_LOG(GetTimerLog(), PR_LOG_DEBUG
,
356 ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor
)));
363 mMonitor
.Wait(waitFor
);
365 forceRunNextTimer
= false;
374 TimerThread::AddTimer(nsTimerImpl
* aTimer
)
376 MonitorAutoLock
lock(mMonitor
);
378 // Add the timer to our list.
379 int32_t i
= AddTimerInternal(aTimer
);
381 return NS_ERROR_OUT_OF_MEMORY
;
384 // Awaken the timer thread.
385 if (mWaiting
&& i
== 0) {
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
);
404 return NS_ERROR_OUT_OF_MEMORY
;
407 // Awaken the timer thread.
408 if (mWaiting
&& i
== 0) {
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.
441 // This function must be called from within a lock
443 TimerThread::AddTimerInternal(nsTimerImpl
* aTimer
)
449 TimeStamp now
= TimeStamp::Now();
451 TimerAdditionComparator
c(now
, aTimer
);
452 nsTimerImpl
** insertSlot
= mTimers
.InsertElementSorted(aTimer
, c
);
458 aTimer
->mArmed
= true;
461 #ifdef MOZ_TASK_TRACER
462 // Create a FakeTracedTask, and dispatch it here. This is the start point of
464 aTimer
->DispatchTracedTask();
467 return insertSlot
- mTimers
.Elements();
471 TimerThread::RemoveTimerInternal(nsTimerImpl
* aTimer
)
473 if (!mTimers
.RemoveElement(aTimer
)) {
477 ReleaseTimerInternal(aTimer
);
482 TimerThread::ReleaseTimerInternal(nsTimerImpl
* aTimer
)
484 // Order is crucial here -- see nsTimerImpl::Release.
485 aTimer
->mArmed
= false;
490 TimerThread::DoBeforeSleep()
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
503 timer
->GetDelay(&delay
);
504 timer
->SetDelay(delay
);
511 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
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) {
519 } else if (strcmp(aTopic
, "wake_notification") == 0 ||
520 strcmp(aTopic
, "resume_process_notification") == 0) {