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"
11 #include "TimerThread.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/IntegerPrintfMacros.h"
14 #include "mozilla/Logging.h"
15 #include "mozilla/Mutex.h"
16 #include "mozilla/ProfilerLabels.h"
17 #include "mozilla/ResultExtensions.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/StaticMutex.h"
20 #include "nsThreadManager.h"
21 #include "nsThreadUtils.h"
27 # define getpid _getpid
33 using mozilla::Atomic
;
34 using mozilla::LogLevel
;
35 using mozilla::MakeRefPtr
;
36 using mozilla::MutexAutoLock
;
37 using mozilla::TimeDuration
;
38 using mozilla::TimeStamp
;
40 // Holds the timer thread and manages all interactions with it
41 // under a locked mutex. This wrapper is not destroyed until after
42 // nsThreadManager shutdown to ensure we don't UAF during an offthread access to
44 class TimerThreadWrapper
{
46 constexpr TimerThreadWrapper() : mThread(nullptr){};
47 ~TimerThreadWrapper() = default;
52 nsresult
AddTimer(nsTimerImpl
* aTimer
, const MutexAutoLock
& aProofOfLock
);
53 nsresult
RemoveTimer(nsTimerImpl
* aTimer
, const MutexAutoLock
& aProofOfLock
);
54 TimeStamp
FindNextFireTimeForCurrentThread(TimeStamp aDefault
,
55 uint32_t aSearchBound
);
56 uint32_t AllowedEarlyFiringMicroseconds();
59 static mozilla::StaticMutex sMutex
;
63 mozilla::StaticMutex
TimerThreadWrapper::sMutex
;
65 nsresult
TimerThreadWrapper::Init() {
67 mozilla::StaticMutexAutoLock
lock(sMutex
);
68 mThread
= new TimerThread();
71 rv
= mThread
->InitLocks();
80 void TimerThreadWrapper::Shutdown() {
81 RefPtr
<TimerThread
> thread
;
84 mozilla::StaticMutexAutoLock
lock(sMutex
);
90 // Shutdown calls |nsTimerImpl::Cancel| which needs to make a call into
91 // |RemoveTimer|. This can't be done under the lock.
95 mozilla::StaticMutexAutoLock
lock(sMutex
);
100 nsresult
TimerThreadWrapper::AddTimer(nsTimerImpl
* aTimer
,
101 const MutexAutoLock
& aProofOfLock
) {
102 mozilla::StaticMutexAutoLock
lock(sMutex
);
104 return mThread
->AddTimer(aTimer
, aProofOfLock
);
106 return NS_ERROR_NOT_AVAILABLE
;
109 nsresult
TimerThreadWrapper::RemoveTimer(nsTimerImpl
* aTimer
,
110 const MutexAutoLock
& aProofOfLock
) {
111 mozilla::StaticMutexAutoLock
lock(sMutex
);
113 return mThread
->RemoveTimer(aTimer
, aProofOfLock
);
115 return NS_ERROR_NOT_AVAILABLE
;
118 TimeStamp
TimerThreadWrapper::FindNextFireTimeForCurrentThread(
119 TimeStamp aDefault
, uint32_t aSearchBound
) {
120 mozilla::StaticMutexAutoLock
lock(sMutex
);
122 ? mThread
->FindNextFireTimeForCurrentThread(aDefault
, aSearchBound
)
126 uint32_t TimerThreadWrapper::AllowedEarlyFiringMicroseconds() {
127 mozilla::StaticMutexAutoLock
lock(sMutex
);
128 return mThread
? mThread
->AllowedEarlyFiringMicroseconds() : 0;
131 static TimerThreadWrapper gThreadWrapper
;
133 // This module prints info about the precision of timers.
134 static mozilla::LazyLogModule
sTimerLog("nsTimerImpl");
136 mozilla::LogModule
* GetTimerLog() { return sTimerLog
; }
138 TimeStamp
NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault
,
139 uint32_t aSearchBound
) {
140 return gThreadWrapper
.FindNextFireTimeForCurrentThread(aDefault
,
144 already_AddRefed
<nsITimer
> NS_NewTimer() { return NS_NewTimer(nullptr); }
146 already_AddRefed
<nsITimer
> NS_NewTimer(nsIEventTarget
* aTarget
) {
147 return nsTimer::WithEventTarget(aTarget
).forget();
150 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithObserver(
151 nsIObserver
* aObserver
, uint32_t aDelay
, uint32_t aType
,
152 nsIEventTarget
* aTarget
) {
153 nsCOMPtr
<nsITimer
> timer
;
154 MOZ_TRY(NS_NewTimerWithObserver(getter_AddRefs(timer
), aObserver
, aDelay
,
156 return std::move(timer
);
158 nsresult
NS_NewTimerWithObserver(nsITimer
** aTimer
, nsIObserver
* aObserver
,
159 uint32_t aDelay
, uint32_t aType
,
160 nsIEventTarget
* aTarget
) {
161 auto timer
= nsTimer::WithEventTarget(aTarget
);
163 MOZ_TRY(timer
->Init(aObserver
, aDelay
, aType
));
164 timer
.forget(aTimer
);
168 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
169 nsITimerCallback
* aCallback
, uint32_t aDelay
, uint32_t aType
,
170 nsIEventTarget
* aTarget
) {
171 nsCOMPtr
<nsITimer
> timer
;
172 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), aCallback
, aDelay
,
174 return std::move(timer
);
176 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
, nsITimerCallback
* aCallback
,
177 uint32_t aDelay
, uint32_t aType
,
178 nsIEventTarget
* aTarget
) {
179 auto timer
= nsTimer::WithEventTarget(aTarget
);
181 MOZ_TRY(timer
->InitWithCallback(aCallback
, aDelay
, aType
));
182 timer
.forget(aTimer
);
186 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
187 nsITimerCallback
* aCallback
, const TimeDuration
& aDelay
, uint32_t aType
,
188 nsIEventTarget
* aTarget
) {
189 nsCOMPtr
<nsITimer
> timer
;
190 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), aCallback
, aDelay
,
192 return std::move(timer
);
194 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
, nsITimerCallback
* aCallback
,
195 const TimeDuration
& aDelay
, uint32_t aType
,
196 nsIEventTarget
* aTarget
) {
197 auto timer
= nsTimer::WithEventTarget(aTarget
);
199 MOZ_TRY(timer
->InitHighResolutionWithCallback(aCallback
, aDelay
, aType
));
200 timer
.forget(aTimer
);
204 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
205 std::function
<void(nsITimer
*)>&& aCallback
, uint32_t aDelay
, uint32_t aType
,
206 const char* aNameString
, nsIEventTarget
* aTarget
) {
207 nsCOMPtr
<nsITimer
> timer
;
208 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), std::move(aCallback
),
209 aDelay
, aType
, aNameString
, aTarget
));
212 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
,
213 std::function
<void(nsITimer
*)>&& aCallback
,
214 uint32_t aDelay
, uint32_t aType
,
215 const char* aNameString
,
216 nsIEventTarget
* aTarget
) {
217 return NS_NewTimerWithCallback(aTimer
, std::move(aCallback
),
218 TimeDuration::FromMilliseconds(aDelay
), aType
,
219 aNameString
, aTarget
);
222 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
223 std::function
<void(nsITimer
*)>&& aCallback
, const TimeDuration
& aDelay
,
224 uint32_t aType
, const char* aNameString
, nsIEventTarget
* aTarget
) {
225 nsCOMPtr
<nsITimer
> timer
;
226 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), std::move(aCallback
),
227 aDelay
, aType
, aNameString
, aTarget
));
230 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
,
231 std::function
<void(nsITimer
*)>&& aCallback
,
232 const TimeDuration
& aDelay
, uint32_t aType
,
233 const char* aNameString
,
234 nsIEventTarget
* aTarget
) {
235 RefPtr
<nsTimer
> timer
= nsTimer::WithEventTarget(aTarget
);
237 MOZ_TRY(timer
->InitWithClosureCallback(std::move(aCallback
), aDelay
, aType
,
239 timer
.forget(aTimer
);
243 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithFuncCallback(
244 nsTimerCallbackFunc aCallback
, void* aClosure
, uint32_t aDelay
,
245 uint32_t aType
, const char* aNameString
, nsIEventTarget
* aTarget
) {
246 nsCOMPtr
<nsITimer
> timer
;
247 MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer
), aCallback
,
248 aClosure
, aDelay
, aType
, aNameString
,
250 return std::move(timer
);
252 nsresult
NS_NewTimerWithFuncCallback(nsITimer
** aTimer
,
253 nsTimerCallbackFunc aCallback
,
254 void* aClosure
, uint32_t aDelay
,
255 uint32_t aType
, const char* aNameString
,
256 nsIEventTarget
* aTarget
) {
257 auto timer
= nsTimer::WithEventTarget(aTarget
);
259 MOZ_TRY(timer
->InitWithNamedFuncCallback(aCallback
, aClosure
, aDelay
, aType
,
261 timer
.forget(aTimer
);
265 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithFuncCallback(
266 nsTimerCallbackFunc aCallback
, void* aClosure
, const TimeDuration
& aDelay
,
267 uint32_t aType
, const char* aNameString
, nsIEventTarget
* aTarget
) {
268 nsCOMPtr
<nsITimer
> timer
;
269 MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer
), aCallback
,
270 aClosure
, aDelay
, aType
, aNameString
,
272 return std::move(timer
);
274 nsresult
NS_NewTimerWithFuncCallback(nsITimer
** aTimer
,
275 nsTimerCallbackFunc aCallback
,
276 void* aClosure
, const TimeDuration
& aDelay
,
277 uint32_t aType
, const char* aNameString
,
278 nsIEventTarget
* aTarget
) {
279 auto timer
= nsTimer::WithEventTarget(aTarget
);
281 MOZ_TRY(timer
->InitHighResolutionWithNamedFuncCallback(
282 aCallback
, aClosure
, aDelay
, aType
, aNameString
));
283 timer
.forget(aTimer
);
287 // This module prints info about which timers are firing, which is useful for
288 // wakeups for the purposes of power profiling. Set the following environment
289 // variable before starting the browser.
291 // MOZ_LOG=TimerFirings:4
293 // Then a line will be printed for every timer that fires.
295 // If you redirect this output to a file called "out", you can then
296 // post-process it with a command something like the following.
298 // cat out | grep timer | sort | uniq -c | sort -r -n
300 // This will show how often each unique line appears, with the most common ones
303 // More detailed docs are here:
304 // https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
306 static mozilla::LazyLogModule
sTimerFiringsLog("TimerFirings");
308 static mozilla::LogModule
* GetTimerFiringsLog() { return sTimerFiringsLog
; }
313 mozilla::StaticMutex
nsTimerImpl::sDeltaMutex
;
315 double nsTimerImpl::sDeltaSumSquared
= 0;
317 double nsTimerImpl::sDeltaSum
= 0;
319 double nsTimerImpl::sDeltaNum
= 0;
321 static void myNS_MeanAndStdDev(double n
, double sumOfValues
,
322 double sumOfSquaredValues
, double* meanResult
,
323 double* stdDevResult
) {
324 double mean
= 0.0, var
= 0.0, stdDev
= 0.0;
325 if (n
> 0.0 && sumOfValues
>= 0) {
326 mean
= sumOfValues
/ n
;
327 double temp
= (n
* sumOfSquaredValues
) - (sumOfValues
* sumOfValues
);
328 if (temp
< 0.0 || n
<= 1) {
331 var
= temp
/ (n
* (n
- 1));
333 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
334 stdDev
= var
!= 0.0 ? sqrt(var
) : 0.0;
337 *stdDevResult
= stdDev
;
340 NS_IMPL_QUERY_INTERFACE(nsTimer
, nsITimer
)
341 NS_IMPL_ADDREF(nsTimer
)
343 NS_IMETHODIMP_(MozExternalRefCountType
)
344 nsTimer::Release(void) {
345 nsrefcnt count
= --mRefCnt
;
346 NS_LOG_RELEASE(this, count
, "nsTimer");
349 // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
350 mImpl
->CancelImpl(true);
351 } else if (count
== 0) {
358 nsTimerImpl::nsTimerImpl(nsITimer
* aTimer
, nsIEventTarget
* aTarget
)
359 : mEventTarget(aTarget
),
364 mMutex("nsTimerImpl::mMutex"),
365 mCallback(UnknownCallback
{}),
367 // XXX some code creates timers during xpcom shutdown, when threads are no
368 // longer available, so we cannot turn this on yet.
369 // MOZ_ASSERT(mEventTarget);
373 nsresult
nsTimerImpl::Startup() { return gThreadWrapper
.Init(); }
375 void nsTimerImpl::Shutdown() {
376 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
)) {
377 mozilla::StaticMutexAutoLock
lock(sDeltaMutex
);
378 double mean
= 0, stddev
= 0;
379 myNS_MeanAndStdDev(sDeltaNum
, sDeltaSum
, sDeltaSumSquared
, &mean
, &stddev
);
381 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
382 ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
383 sDeltaNum
, sDeltaSum
, sDeltaSumSquared
));
384 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
385 ("mean: %fms, stddev: %fms\n", mean
, stddev
));
388 gThreadWrapper
.Shutdown();
391 nsresult
nsTimerImpl::InitCommon(const TimeDuration
& aDelay
, uint32_t aType
,
392 Callback
&& newCallback
,
393 const MutexAutoLock
& aProofOfLock
) {
395 NS_ERROR("mEventTarget is NULL");
396 return NS_ERROR_NOT_INITIALIZED
;
399 gThreadWrapper
.RemoveTimer(this, aProofOfLock
);
401 // If we have an existing callback, using `swap` ensures it's destroyed after
402 // the mutex is unlocked in our caller.
403 std::swap(mCallback
, newCallback
);
406 mType
= (uint8_t)aType
;
408 mTimeout
= TimeStamp::Now() + mDelay
;
410 return gThreadWrapper
.AddTimer(this, aProofOfLock
);
413 nsresult
nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc
,
414 void* aClosure
, uint32_t aDelay
,
417 return InitHighResolutionWithNamedFuncCallback(
418 aFunc
, aClosure
, TimeDuration::FromMilliseconds(aDelay
), aType
, aName
);
421 nsresult
nsTimerImpl::InitHighResolutionWithNamedFuncCallback(
422 nsTimerCallbackFunc aFunc
, void* aClosure
, const TimeDuration
& aDelay
,
423 uint32_t aType
, const char* aName
) {
424 if (NS_WARN_IF(!aFunc
)) {
425 return NS_ERROR_INVALID_ARG
;
428 Callback cb
{FuncCallback
{aFunc
, aClosure
, aName
}};
430 MutexAutoLock
lock(mMutex
);
431 return InitCommon(aDelay
, aType
, std::move(cb
), lock
);
434 nsresult
nsTimerImpl::InitWithCallback(nsITimerCallback
* aCallback
,
435 uint32_t aDelayInMs
, uint32_t aType
) {
436 return InitHighResolutionWithCallback(
437 aCallback
, TimeDuration::FromMilliseconds(aDelayInMs
), aType
);
440 nsresult
nsTimerImpl::InitHighResolutionWithCallback(
441 nsITimerCallback
* aCallback
, const TimeDuration
& aDelay
, uint32_t aType
) {
442 if (NS_WARN_IF(!aCallback
)) {
443 return NS_ERROR_INVALID_ARG
;
446 // Goes out of scope after the unlock, prevents deadlock
447 Callback cb
{nsCOMPtr
{aCallback
}};
449 MutexAutoLock
lock(mMutex
);
450 return InitCommon(aDelay
, aType
, std::move(cb
), lock
);
453 nsresult
nsTimerImpl::Init(nsIObserver
* aObserver
, uint32_t aDelayInMs
,
455 if (NS_WARN_IF(!aObserver
)) {
456 return NS_ERROR_INVALID_ARG
;
459 Callback cb
{nsCOMPtr
{aObserver
}};
461 MutexAutoLock
lock(mMutex
);
462 return InitCommon(TimeDuration::FromMilliseconds(aDelayInMs
), aType
,
463 std::move(cb
), lock
);
466 nsresult
nsTimerImpl::InitWithClosureCallback(
467 std::function
<void(nsITimer
*)>&& aCallback
, const TimeDuration
& aDelay
,
468 uint32_t aType
, const char* aNameString
) {
469 if (NS_WARN_IF(!aCallback
)) {
470 return NS_ERROR_INVALID_ARG
;
473 Callback cb
{ClosureCallback
{std::move(aCallback
), aNameString
}};
475 MutexAutoLock
lock(mMutex
);
476 return InitCommon(aDelay
, aType
, std::move(cb
), lock
);
479 nsresult
nsTimerImpl::Cancel() {
484 void nsTimerImpl::CancelImpl(bool aClearITimer
) {
485 Callback cbTrash
{UnknownCallback
{}};
486 RefPtr
<nsITimer
> timerTrash
;
489 MutexAutoLock
lock(mMutex
);
490 gThreadWrapper
.RemoveTimer(this, lock
);
492 // The swap ensures our callback isn't dropped until after the mutex is
494 std::swap(cbTrash
, mCallback
);
497 // Don't clear this if we're firing; once Fire returns, we'll get this call
499 if (aClearITimer
&& !mFiring
) {
502 "mITimer was nulled already! "
503 "This indicates that someone has messed up the refcount on nsTimer!");
504 timerTrash
.swap(mITimer
);
509 nsresult
nsTimerImpl::SetDelay(uint32_t aDelay
) {
510 MutexAutoLock
lock(mMutex
);
511 if (GetCallback().is
<UnknownCallback
>() && !IsRepeating()) {
512 // This may happen if someone tries to re-use a one-shot timer
513 // by re-setting delay instead of reinitializing the timer.
515 "nsITimer->SetDelay() called when the "
516 "one-shot timer is not set up.");
517 return NS_ERROR_NOT_INITIALIZED
;
521 reAdd
= NS_SUCCEEDED(gThreadWrapper
.RemoveTimer(this, lock
));
523 mDelay
= TimeDuration::FromMilliseconds(aDelay
);
524 mTimeout
= TimeStamp::Now() + mDelay
;
527 gThreadWrapper
.AddTimer(this, lock
);
533 nsresult
nsTimerImpl::GetDelay(uint32_t* aDelay
) {
534 MutexAutoLock
lock(mMutex
);
535 *aDelay
= mDelay
.ToMilliseconds();
539 nsresult
nsTimerImpl::SetType(uint32_t aType
) {
540 MutexAutoLock
lock(mMutex
);
541 mType
= (uint8_t)aType
;
542 // XXX if this is called, we should change the actual type.. this could effect
543 // repeating timers. we need to ensure in Fire() that if mType has changed
544 // during the callback that we don't end up with the timer in the queue twice.
548 nsresult
nsTimerImpl::GetType(uint32_t* aType
) {
549 MutexAutoLock
lock(mMutex
);
554 nsresult
nsTimerImpl::GetClosure(void** aClosure
) {
555 MutexAutoLock
lock(mMutex
);
556 if (GetCallback().is
<FuncCallback
>()) {
557 *aClosure
= GetCallback().as
<FuncCallback
>().mClosure
;
564 nsresult
nsTimerImpl::GetCallback(nsITimerCallback
** aCallback
) {
565 MutexAutoLock
lock(mMutex
);
566 if (GetCallback().is
<InterfaceCallback
>()) {
567 NS_IF_ADDREF(*aCallback
= GetCallback().as
<InterfaceCallback
>());
569 *aCallback
= nullptr;
574 nsresult
nsTimerImpl::GetTarget(nsIEventTarget
** aTarget
) {
575 MutexAutoLock
lock(mMutex
);
576 NS_IF_ADDREF(*aTarget
= mEventTarget
);
580 nsresult
nsTimerImpl::SetTarget(nsIEventTarget
* aTarget
) {
581 MutexAutoLock
lock(mMutex
);
582 if (NS_WARN_IF(!mCallback
.is
<UnknownCallback
>())) {
583 return NS_ERROR_ALREADY_INITIALIZED
;
587 mEventTarget
= aTarget
;
589 mEventTarget
= mozilla::GetCurrentSerialEventTarget();
594 nsresult
nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut
) {
595 *aValueOut
= gThreadWrapper
.AllowedEarlyFiringMicroseconds();
599 void nsTimerImpl::Fire(int32_t aGeneration
) {
602 TimeStamp oldTimeout
;
603 Callback callbackDuringFire
{UnknownCallback
{}};
604 nsCOMPtr
<nsITimer
> timer
;
607 // Don't fire callbacks or fiddle with refcounts when the mutex is locked.
608 // If some other thread Cancels/Inits after this, they're just too late.
609 MutexAutoLock
lock(mMutex
);
610 if (aGeneration
!= mGeneration
) {
611 // This timer got rescheduled or cancelled before we fired, so ignore this
616 // We modify mTimeout, so we must not be in the current TimerThread's
617 // mTimers list. Adding to that list calls SetHolder(), so use mHolder
618 // as a proxy to know if we're in the list
619 MOZ_ASSERT(!mHolder
);
622 callbackDuringFire
= mCallback
;
624 oldDelay
= mDelay
.ToMilliseconds();
625 oldTimeout
= mTimeout
;
626 // Ensure that the nsITimer does not unhook from the nsTimerImpl during
627 // Fire; this will cause null pointer crashes if the user of the timer drops
628 // its reference, and then uses the nsITimer* passed in the callback.
632 AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER
);
634 TimeStamp fireTime
= TimeStamp::Now();
635 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
)) {
636 TimeDuration delta
= fireTime
- oldTimeout
;
637 int32_t d
= delta
.ToMilliseconds(); // delta in ms
639 mozilla::StaticMutexAutoLock
lock(sDeltaMutex
);
641 sDeltaSumSquared
+= double(d
) * double(d
);
645 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
646 ("[this=%p] expected delay time %4ums\n", this, oldDelay
));
647 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
648 ("[this=%p] actual delay time %4dms\n", this, oldDelay
+ d
));
649 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
650 ("[this=%p] (mType is %d) -------\n", this, oldType
));
651 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
652 ("[this=%p] delta %4dms\n", this, d
));
655 if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug
)) {
656 LogFiring(callbackDuringFire
, oldType
, oldDelay
);
659 callbackDuringFire
.match(
660 [](const UnknownCallback
&) {},
661 [&](const InterfaceCallback
& i
) { i
->Notify(timer
); },
662 [&](const ObserverCallback
& o
) {
663 o
->Observe(timer
, NS_TIMER_CALLBACK_TOPIC
, nullptr);
665 [&](const FuncCallback
& f
) { f
.mFunc(timer
, f
.mClosure
); },
666 [&](const ClosureCallback
& c
) { c
.mFunc(timer
); });
668 TimeStamp now
= TimeStamp::Now();
670 MutexAutoLock
lock(mMutex
);
671 if (aGeneration
== mGeneration
) {
673 // Repeating timer has not been re-init or canceled; reschedule
675 mTimeout
= now
+ mDelay
;
678 // If we are late enough finishing the callback that we have missed
679 // some firings, do not attempt to play catchup, just get back on the
680 // cadence we're supposed to maintain.
681 unsigned missedFirings
=
682 static_cast<unsigned>((now
- mTimeout
) / mDelay
);
683 mTimeout
+= mDelay
* (missedFirings
+ 1);
685 // Can we stop allowing repeating timers with delay 0?
689 gThreadWrapper
.AddTimer(this, lock
);
691 // Non-repeating timer that has not been re-scheduled. Clear.
692 // XXX(nika): Other callsites seem to go to some effort to avoid
693 // destroying mCallback when it's held. Why not this one?
694 mCallback
= mozilla::AsVariant(UnknownCallback
{});
700 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
701 ("[this=%p] Took %fms to fire timer callback\n", this,
702 (now
- fireTime
).ToMilliseconds()));
705 // See the big comment above GetTimerFiringsLog() to understand this code.
706 void nsTimerImpl::LogFiring(const Callback
& aCallback
, uint8_t aType
,
710 case nsITimer::TYPE_ONE_SHOT
:
711 typeStr
= "ONE_SHOT ";
713 case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY
:
714 typeStr
= "ONE_LOW ";
716 case nsITimer::TYPE_REPEATING_SLACK
:
719 case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY
:
720 typeStr
= "SLACK_LOW ";
722 case nsITimer::TYPE_REPEATING_PRECISE
: /* fall through */
723 case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP
:
724 typeStr
= "PRECISE ";
727 MOZ_CRASH("bad type");
731 [&](const UnknownCallback
&) {
733 GetTimerFiringsLog(), LogLevel::Debug
,
734 ("[%d] ??? timer (%s, %5d ms)\n", getpid(), typeStr
, aDelay
));
736 [&](const InterfaceCallback
& i
) {
737 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
738 ("[%d] iface timer (%s %5d ms): %p\n", getpid(), typeStr
,
741 [&](const ObserverCallback
& o
) {
742 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
743 ("[%d] obs timer (%s %5d ms): %p\n", getpid(), typeStr
,
746 [&](const FuncCallback
& f
) {
747 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
748 ("[%d] fn timer (%s %5d ms): %s\n", getpid(), typeStr
,
751 [&](const ClosureCallback
& c
) {
752 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
753 ("[%d] closure timer (%s %5d ms): %s\n", getpid(), typeStr
,
758 void nsTimerImpl::GetName(nsACString
& aName
,
759 const MutexAutoLock
& aProofOfLock
) {
761 [&](const UnknownCallback
&) { aName
.AssignLiteral("Canceled_timer"); },
762 [&](const InterfaceCallback
& i
) {
763 if (nsCOMPtr
<nsINamed
> named
= do_QueryInterface(i
)) {
764 named
->GetName(aName
);
766 aName
.AssignLiteral("Anonymous_interface_timer");
769 [&](const ObserverCallback
& o
) {
770 if (nsCOMPtr
<nsINamed
> named
= do_QueryInterface(o
)) {
771 named
->GetName(aName
);
773 aName
.AssignLiteral("Anonymous_observer_timer");
776 [&](const FuncCallback
& f
) { aName
.Assign(f
.mName
); },
777 [&](const ClosureCallback
& c
) { aName
.Assign(c
.mName
); });
780 void nsTimerImpl::GetName(nsACString
& aName
) {
781 MutexAutoLock
lock(mMutex
);
782 GetName(aName
, lock
);
785 void nsTimerImpl::SetHolder(nsTimerImplHolder
* aHolder
) { mHolder
= aHolder
; }
787 nsTimer::~nsTimer() = default;
789 size_t nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const {
790 return aMallocSizeOf(this);
794 RefPtr
<nsTimer
> nsTimer::WithEventTarget(nsIEventTarget
* aTarget
) {
796 aTarget
= mozilla::GetCurrentSerialEventTarget();
798 return do_AddRef(new nsTimer(aTarget
));
802 nsresult
nsTimer::XPCOMConstructor(nsISupports
* aOuter
, REFNSIID aIID
,
805 if (aOuter
!= nullptr) {
806 return NS_ERROR_NO_AGGREGATION
;
809 auto timer
= WithEventTarget(nullptr);
811 return timer
->QueryInterface(aIID
, aResult
);