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 "mozilla/Try.h"
21 #include "nsThreadManager.h"
22 #include "nsThreadUtils.h"
28 # define getpid _getpid
34 using mozilla::Atomic
;
35 using mozilla::LogLevel
;
36 using mozilla::MakeRefPtr
;
37 using mozilla::MutexAutoLock
;
38 using mozilla::TimeDuration
;
39 using mozilla::TimeStamp
;
41 // Holds the timer thread and manages all interactions with it
42 // under a locked mutex. This wrapper is not destroyed until after
43 // nsThreadManager shutdown to ensure we don't UAF during an offthread access to
45 class TimerThreadWrapper
{
47 constexpr TimerThreadWrapper() : mThread(nullptr) {};
48 ~TimerThreadWrapper() = default;
53 nsresult
AddTimer(nsTimerImpl
* aTimer
, const MutexAutoLock
& aProofOfLock
)
54 MOZ_REQUIRES(aTimer
->mMutex
);
55 nsresult
RemoveTimer(nsTimerImpl
* aTimer
, const MutexAutoLock
& aProofOfLock
)
56 MOZ_REQUIRES(aTimer
->mMutex
);
57 TimeStamp
FindNextFireTimeForCurrentThread(TimeStamp aDefault
,
58 uint32_t aSearchBound
);
59 uint32_t AllowedEarlyFiringMicroseconds();
60 nsresult
GetTimers(nsTArray
<RefPtr
<nsITimer
>>& aRetVal
);
63 static mozilla::StaticMutex sMutex
;
64 TimerThread
* mThread
MOZ_GUARDED_BY(sMutex
);
67 mozilla::StaticMutex
TimerThreadWrapper::sMutex
;
69 nsresult
TimerThreadWrapper::Init() {
70 mozilla::StaticMutexAutoLock
lock(sMutex
);
71 mThread
= new TimerThread();
78 void TimerThreadWrapper::Shutdown() {
79 RefPtr
<TimerThread
> thread
;
82 mozilla::StaticMutexAutoLock
lock(sMutex
);
88 // Shutdown calls |nsTimerImpl::Cancel| which needs to make a call into
89 // |RemoveTimer|. This can't be done under the lock.
93 mozilla::StaticMutexAutoLock
lock(sMutex
);
98 nsresult
TimerThreadWrapper::AddTimer(nsTimerImpl
* aTimer
,
99 const MutexAutoLock
& aProofOfLock
) {
100 mozilla::StaticMutexAutoLock
lock(sMutex
);
102 return mThread
->AddTimer(aTimer
, aProofOfLock
);
104 return NS_ERROR_NOT_AVAILABLE
;
107 nsresult
TimerThreadWrapper::RemoveTimer(nsTimerImpl
* aTimer
,
108 const MutexAutoLock
& aProofOfLock
) {
109 mozilla::StaticMutexAutoLock
lock(sMutex
);
111 return mThread
->RemoveTimer(aTimer
, aProofOfLock
);
113 return NS_ERROR_NOT_AVAILABLE
;
116 TimeStamp
TimerThreadWrapper::FindNextFireTimeForCurrentThread(
117 TimeStamp aDefault
, uint32_t aSearchBound
) {
118 mozilla::StaticMutexAutoLock
lock(sMutex
);
120 ? mThread
->FindNextFireTimeForCurrentThread(aDefault
, aSearchBound
)
124 uint32_t TimerThreadWrapper::AllowedEarlyFiringMicroseconds() {
125 mozilla::StaticMutexAutoLock
lock(sMutex
);
126 return mThread
? mThread
->AllowedEarlyFiringMicroseconds() : 0;
129 nsresult
TimerThreadWrapper::GetTimers(nsTArray
<RefPtr
<nsITimer
>>& aRetVal
) {
130 RefPtr
<TimerThread
> thread
;
132 mozilla::StaticMutexAutoLock
lock(sMutex
);
134 return NS_ERROR_NOT_AVAILABLE
;
138 return thread
->GetTimers(aRetVal
);
141 static TimerThreadWrapper gThreadWrapper
;
143 // This module prints info about the precision of timers.
144 static mozilla::LazyLogModule
sTimerLog("nsTimerImpl");
146 mozilla::LogModule
* GetTimerLog() { return sTimerLog
; }
148 TimeStamp
NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault
,
149 uint32_t aSearchBound
) {
150 return gThreadWrapper
.FindNextFireTimeForCurrentThread(aDefault
,
154 already_AddRefed
<nsITimer
> NS_NewTimer() { return NS_NewTimer(nullptr); }
156 already_AddRefed
<nsITimer
> NS_NewTimer(nsIEventTarget
* aTarget
) {
157 return nsTimer::WithEventTarget(aTarget
).forget();
160 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithObserver(
161 nsIObserver
* aObserver
, uint32_t aDelay
, uint32_t aType
,
162 nsIEventTarget
* aTarget
) {
163 nsCOMPtr
<nsITimer
> timer
;
164 MOZ_TRY(NS_NewTimerWithObserver(getter_AddRefs(timer
), aObserver
, aDelay
,
166 return std::move(timer
);
168 nsresult
NS_NewTimerWithObserver(nsITimer
** aTimer
, nsIObserver
* aObserver
,
169 uint32_t aDelay
, uint32_t aType
,
170 nsIEventTarget
* aTarget
) {
171 auto timer
= nsTimer::WithEventTarget(aTarget
);
173 MOZ_TRY(timer
->Init(aObserver
, aDelay
, aType
));
174 timer
.forget(aTimer
);
178 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
179 nsITimerCallback
* aCallback
, uint32_t aDelay
, uint32_t aType
,
180 nsIEventTarget
* aTarget
) {
181 nsCOMPtr
<nsITimer
> timer
;
182 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), aCallback
, aDelay
,
184 return std::move(timer
);
186 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
, nsITimerCallback
* aCallback
,
187 uint32_t aDelay
, uint32_t aType
,
188 nsIEventTarget
* aTarget
) {
189 auto timer
= nsTimer::WithEventTarget(aTarget
);
191 MOZ_TRY(timer
->InitWithCallback(aCallback
, aDelay
, aType
));
192 timer
.forget(aTimer
);
196 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
197 nsITimerCallback
* aCallback
, const TimeDuration
& aDelay
, uint32_t aType
,
198 nsIEventTarget
* aTarget
) {
199 nsCOMPtr
<nsITimer
> timer
;
200 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), aCallback
, aDelay
,
202 return std::move(timer
);
204 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
, nsITimerCallback
* aCallback
,
205 const TimeDuration
& aDelay
, uint32_t aType
,
206 nsIEventTarget
* aTarget
) {
207 auto timer
= nsTimer::WithEventTarget(aTarget
);
209 MOZ_TRY(timer
->InitHighResolutionWithCallback(aCallback
, aDelay
, aType
));
210 timer
.forget(aTimer
);
214 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
215 std::function
<void(nsITimer
*)>&& aCallback
, uint32_t aDelay
, uint32_t aType
,
216 const char* aNameString
, nsIEventTarget
* aTarget
) {
217 nsCOMPtr
<nsITimer
> timer
;
218 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), std::move(aCallback
),
219 aDelay
, aType
, aNameString
, aTarget
));
222 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
,
223 std::function
<void(nsITimer
*)>&& aCallback
,
224 uint32_t aDelay
, uint32_t aType
,
225 const char* aNameString
,
226 nsIEventTarget
* aTarget
) {
227 return NS_NewTimerWithCallback(aTimer
, std::move(aCallback
),
228 TimeDuration::FromMilliseconds(aDelay
), aType
,
229 aNameString
, aTarget
);
232 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithCallback(
233 std::function
<void(nsITimer
*)>&& aCallback
, const TimeDuration
& aDelay
,
234 uint32_t aType
, const char* aNameString
, nsIEventTarget
* aTarget
) {
235 nsCOMPtr
<nsITimer
> timer
;
236 MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer
), std::move(aCallback
),
237 aDelay
, aType
, aNameString
, aTarget
));
240 nsresult
NS_NewTimerWithCallback(nsITimer
** aTimer
,
241 std::function
<void(nsITimer
*)>&& aCallback
,
242 const TimeDuration
& aDelay
, uint32_t aType
,
243 const char* aNameString
,
244 nsIEventTarget
* aTarget
) {
245 RefPtr
<nsTimer
> timer
= nsTimer::WithEventTarget(aTarget
);
247 MOZ_TRY(timer
->InitWithClosureCallback(std::move(aCallback
), aDelay
, aType
,
249 timer
.forget(aTimer
);
253 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithFuncCallback(
254 nsTimerCallbackFunc aCallback
, void* aClosure
, uint32_t aDelay
,
255 uint32_t aType
, const char* aNameString
, nsIEventTarget
* aTarget
) {
256 nsCOMPtr
<nsITimer
> timer
;
257 MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer
), aCallback
,
258 aClosure
, aDelay
, aType
, aNameString
,
260 return std::move(timer
);
262 nsresult
NS_NewTimerWithFuncCallback(nsITimer
** aTimer
,
263 nsTimerCallbackFunc aCallback
,
264 void* aClosure
, uint32_t aDelay
,
265 uint32_t aType
, const char* aNameString
,
266 nsIEventTarget
* aTarget
) {
267 auto timer
= nsTimer::WithEventTarget(aTarget
);
269 MOZ_TRY(timer
->InitWithNamedFuncCallback(aCallback
, aClosure
, aDelay
, aType
,
271 timer
.forget(aTimer
);
275 mozilla::Result
<nsCOMPtr
<nsITimer
>, nsresult
> NS_NewTimerWithFuncCallback(
276 nsTimerCallbackFunc aCallback
, void* aClosure
, const TimeDuration
& aDelay
,
277 uint32_t aType
, const char* aNameString
, nsIEventTarget
* aTarget
) {
278 nsCOMPtr
<nsITimer
> timer
;
279 MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer
), aCallback
,
280 aClosure
, aDelay
, aType
, aNameString
,
282 return std::move(timer
);
284 nsresult
NS_NewTimerWithFuncCallback(nsITimer
** aTimer
,
285 nsTimerCallbackFunc aCallback
,
286 void* aClosure
, const TimeDuration
& aDelay
,
287 uint32_t aType
, const char* aNameString
,
288 nsIEventTarget
* aTarget
) {
289 auto timer
= nsTimer::WithEventTarget(aTarget
);
291 MOZ_TRY(timer
->InitHighResolutionWithNamedFuncCallback(
292 aCallback
, aClosure
, aDelay
, aType
, aNameString
));
293 timer
.forget(aTimer
);
297 // This module prints info about which timers are firing, which is useful for
298 // wakeups for the purposes of power profiling. Set the following environment
299 // variable before starting the browser.
301 // MOZ_LOG=TimerFirings:4
303 // Then a line will be printed for every timer that fires.
305 // If you redirect this output to a file called "out", you can then
306 // post-process it with a command something like the following.
308 // cat out | grep timer | sort | uniq -c | sort -r -n
310 // This will show how often each unique line appears, with the most common ones
313 // More detailed docs are here:
314 // https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
316 static mozilla::LazyLogModule
sTimerFiringsLog("TimerFirings");
318 static mozilla::LogModule
* GetTimerFiringsLog() { return sTimerFiringsLog
; }
323 mozilla::StaticMutex
nsTimerImpl::sDeltaMutex
;
325 double nsTimerImpl::sDeltaSumSquared
MOZ_GUARDED_BY(nsTimerImpl::sDeltaMutex
) =
328 double nsTimerImpl::sDeltaSum
MOZ_GUARDED_BY(nsTimerImpl::sDeltaMutex
) = 0;
330 double nsTimerImpl::sDeltaNum
MOZ_GUARDED_BY(nsTimerImpl::sDeltaMutex
) = 0;
332 static void myNS_MeanAndStdDev(double n
, double sumOfValues
,
333 double sumOfSquaredValues
, double* meanResult
,
334 double* stdDevResult
) {
335 double mean
= 0.0, var
= 0.0, stdDev
= 0.0;
336 if (n
> 0.0 && sumOfValues
>= 0) {
337 mean
= sumOfValues
/ n
;
338 double temp
= (n
* sumOfSquaredValues
) - (sumOfValues
* sumOfValues
);
339 if (temp
< 0.0 || n
<= 1) {
342 var
= temp
/ (n
* (n
- 1));
344 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
345 stdDev
= var
!= 0.0 ? sqrt(var
) : 0.0;
348 *stdDevResult
= stdDev
;
351 NS_IMPL_QUERY_INTERFACE(nsTimer
, nsITimer
)
352 NS_IMPL_ADDREF(nsTimer
)
354 NS_IMPL_ISUPPORTS(nsTimerManager
, nsITimerManager
)
356 NS_IMETHODIMP
nsTimerManager::GetTimers(nsTArray
<RefPtr
<nsITimer
>>& aRetVal
) {
357 return gThreadWrapper
.GetTimers(aRetVal
);
360 NS_IMETHODIMP_(MozExternalRefCountType
)
361 nsTimer::Release(void) {
362 nsrefcnt count
= --mRefCnt
;
363 NS_LOG_RELEASE(this, count
, "nsTimer");
366 // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
367 mImpl
->CancelImpl(true);
368 } else if (count
== 0) {
375 nsTimerImpl::nsTimerImpl(nsITimer
* aTimer
, nsIEventTarget
* aTarget
)
376 : mEventTarget(aTarget
),
377 mIsInTimerThread(false),
381 mMutex("nsTimerImpl::mMutex"),
382 mCallback(UnknownCallback
{}),
384 // XXX some code creates timers during xpcom shutdown, when threads are no
385 // longer available, so we cannot turn this on yet.
386 // MOZ_ASSERT(mEventTarget);
390 nsresult
nsTimerImpl::Startup() { return gThreadWrapper
.Init(); }
392 void nsTimerImpl::Shutdown() {
393 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
)) {
394 mozilla::StaticMutexAutoLock
lock(sDeltaMutex
);
395 double mean
= 0, stddev
= 0;
396 myNS_MeanAndStdDev(sDeltaNum
, sDeltaSum
, sDeltaSumSquared
, &mean
, &stddev
);
398 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
399 ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
400 sDeltaNum
, sDeltaSum
, sDeltaSumSquared
));
401 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
402 ("mean: %fms, stddev: %fms\n", mean
, stddev
));
405 gThreadWrapper
.Shutdown();
408 nsresult
nsTimerImpl::InitCommon(const TimeDuration
& aDelay
, uint32_t aType
,
409 Callback
&& newCallback
,
410 const MutexAutoLock
& aProofOfLock
) {
412 return NS_ERROR_NOT_INITIALIZED
;
415 gThreadWrapper
.RemoveTimer(this, aProofOfLock
);
417 // If we have an existing callback, using `swap` ensures it's destroyed after
418 // the mutex is unlocked in our caller.
419 std::swap(mCallback
, newCallback
);
422 mType
= (uint8_t)aType
;
424 mTimeout
= TimeStamp::Now() + mDelay
;
426 return gThreadWrapper
.AddTimer(this, aProofOfLock
);
429 nsresult
nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc
,
430 void* aClosure
, uint32_t aDelay
,
433 return InitHighResolutionWithNamedFuncCallback(
434 aFunc
, aClosure
, TimeDuration::FromMilliseconds(aDelay
), aType
, aName
);
437 nsresult
nsTimerImpl::InitHighResolutionWithNamedFuncCallback(
438 nsTimerCallbackFunc aFunc
, void* aClosure
, const TimeDuration
& aDelay
,
439 uint32_t aType
, const char* aName
) {
440 if (NS_WARN_IF(!aFunc
)) {
441 return NS_ERROR_INVALID_ARG
;
444 Callback cb
{FuncCallback
{aFunc
, aClosure
, aName
}};
446 MutexAutoLock
lock(mMutex
);
447 return InitCommon(aDelay
, aType
, std::move(cb
), lock
);
450 nsresult
nsTimerImpl::InitWithCallback(nsITimerCallback
* aCallback
,
451 uint32_t aDelayInMs
, uint32_t aType
) {
452 return InitHighResolutionWithCallback(
453 aCallback
, TimeDuration::FromMilliseconds(aDelayInMs
), aType
);
456 nsresult
nsTimerImpl::InitHighResolutionWithCallback(
457 nsITimerCallback
* aCallback
, const TimeDuration
& aDelay
, uint32_t aType
) {
458 if (NS_WARN_IF(!aCallback
)) {
459 return NS_ERROR_INVALID_ARG
;
462 // Goes out of scope after the unlock, prevents deadlock
463 Callback cb
{nsCOMPtr
{aCallback
}};
465 MutexAutoLock
lock(mMutex
);
466 return InitCommon(aDelay
, aType
, std::move(cb
), lock
);
469 nsresult
nsTimerImpl::Init(nsIObserver
* aObserver
, uint32_t aDelayInMs
,
471 if (NS_WARN_IF(!aObserver
)) {
472 return NS_ERROR_INVALID_ARG
;
475 Callback cb
{nsCOMPtr
{aObserver
}};
477 MutexAutoLock
lock(mMutex
);
478 return InitCommon(TimeDuration::FromMilliseconds(aDelayInMs
), aType
,
479 std::move(cb
), lock
);
482 nsresult
nsTimerImpl::InitWithClosureCallback(
483 std::function
<void(nsITimer
*)>&& aCallback
, const TimeDuration
& aDelay
,
484 uint32_t aType
, const char* aNameString
) {
485 if (NS_WARN_IF(!aCallback
)) {
486 return NS_ERROR_INVALID_ARG
;
489 Callback cb
{ClosureCallback
{std::move(aCallback
), aNameString
}};
491 MutexAutoLock
lock(mMutex
);
492 return InitCommon(aDelay
, aType
, std::move(cb
), lock
);
495 nsresult
nsTimerImpl::Cancel() {
500 void nsTimerImpl::CancelImpl(bool aClearITimer
) {
501 Callback cbTrash
{UnknownCallback
{}};
502 RefPtr
<nsITimer
> timerTrash
;
505 MutexAutoLock
lock(mMutex
);
506 gThreadWrapper
.RemoveTimer(this, lock
);
508 // The swap ensures our callback isn't dropped until after the mutex is
510 std::swap(cbTrash
, mCallback
);
513 // Don't clear this if we're firing; once Fire returns, we'll get this call
515 if (aClearITimer
&& !mFiring
) {
518 "mITimer was nulled already! "
519 "This indicates that someone has messed up the refcount on nsTimer!");
520 timerTrash
.swap(mITimer
);
525 nsresult
nsTimerImpl::SetDelay(uint32_t aDelay
) {
526 MutexAutoLock
lock(mMutex
);
527 if (GetCallback().is
<UnknownCallback
>() && !IsRepeating()) {
528 // This may happen if someone tries to re-use a one-shot timer
529 // by re-setting delay instead of reinitializing the timer.
531 "nsITimer->SetDelay() called when the "
532 "one-shot timer is not set up.");
533 return NS_ERROR_NOT_INITIALIZED
;
537 reAdd
= NS_SUCCEEDED(gThreadWrapper
.RemoveTimer(this, lock
));
539 mDelay
= TimeDuration::FromMilliseconds(aDelay
);
540 mTimeout
= TimeStamp::Now() + mDelay
;
543 gThreadWrapper
.AddTimer(this, lock
);
549 nsresult
nsTimerImpl::GetDelay(uint32_t* aDelay
) {
550 MutexAutoLock
lock(mMutex
);
551 *aDelay
= mDelay
.ToMilliseconds();
555 nsresult
nsTimerImpl::SetType(uint32_t aType
) {
556 MutexAutoLock
lock(mMutex
);
557 mType
= (uint8_t)aType
;
558 // XXX if this is called, we should change the actual type.. this could effect
559 // repeating timers. we need to ensure in Fire() that if mType has changed
560 // during the callback that we don't end up with the timer in the queue twice.
564 nsresult
nsTimerImpl::GetType(uint32_t* aType
) {
565 MutexAutoLock
lock(mMutex
);
570 nsresult
nsTimerImpl::GetClosure(void** aClosure
) {
571 MutexAutoLock
lock(mMutex
);
572 if (GetCallback().is
<FuncCallback
>()) {
573 *aClosure
= GetCallback().as
<FuncCallback
>().mClosure
;
580 nsresult
nsTimerImpl::GetCallback(nsITimerCallback
** aCallback
) {
581 MutexAutoLock
lock(mMutex
);
582 if (GetCallback().is
<InterfaceCallback
>()) {
583 NS_IF_ADDREF(*aCallback
= GetCallback().as
<InterfaceCallback
>());
585 *aCallback
= nullptr;
590 nsresult
nsTimerImpl::GetTarget(nsIEventTarget
** aTarget
) {
591 MutexAutoLock
lock(mMutex
);
592 NS_IF_ADDREF(*aTarget
= mEventTarget
);
596 nsresult
nsTimerImpl::SetTarget(nsIEventTarget
* aTarget
) {
597 MutexAutoLock
lock(mMutex
);
598 if (NS_WARN_IF(!mCallback
.is
<UnknownCallback
>())) {
599 return NS_ERROR_ALREADY_INITIALIZED
;
603 mEventTarget
= aTarget
;
605 mEventTarget
= mozilla::GetCurrentSerialEventTarget();
610 nsresult
nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut
) {
611 *aValueOut
= gThreadWrapper
.AllowedEarlyFiringMicroseconds();
615 void nsTimerImpl::Fire(int32_t aGeneration
) {
618 TimeStamp oldTimeout
;
619 Callback callbackDuringFire
{UnknownCallback
{}};
620 nsCOMPtr
<nsITimer
> timer
;
623 // Don't fire callbacks or fiddle with refcounts when the mutex is locked.
624 // If some other thread Cancels/Inits after this, they're just too late.
625 MutexAutoLock
lock(mMutex
);
626 if (aGeneration
!= mGeneration
) {
627 // This timer got rescheduled or cancelled before we fired, so ignore this
632 // We modify mTimeout, so we must not be in the current TimerThread's
634 MOZ_ASSERT(!mIsInTimerThread
);
637 callbackDuringFire
= mCallback
;
639 oldDelay
= mDelay
.ToMilliseconds();
640 oldTimeout
= mTimeout
;
641 // Ensure that the nsITimer does not unhook from the nsTimerImpl during
642 // Fire; this will cause null pointer crashes if the user of the timer drops
643 // its reference, and then uses the nsITimer* passed in the callback.
647 AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER
);
650 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
)) {
651 fireTime
= TimeStamp::Now();
652 TimeDuration delta
= fireTime
- oldTimeout
;
653 int32_t d
= delta
.ToMilliseconds(); // delta in ms
655 mozilla::StaticMutexAutoLock
lock(sDeltaMutex
);
657 sDeltaSumSquared
+= double(d
) * double(d
);
661 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
662 ("[this=%p] expected delay time %4ums\n", this, oldDelay
));
663 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
664 ("[this=%p] actual delay time %4dms\n", this, oldDelay
+ d
));
665 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
666 ("[this=%p] (mType is %d) -------\n", this, oldType
));
667 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
668 ("[this=%p] delta %4dms\n", this, d
));
671 if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug
)) {
672 LogFiring(callbackDuringFire
, oldType
, oldDelay
);
675 callbackDuringFire
.match(
676 [](const UnknownCallback
&) {},
677 [&](const InterfaceCallback
& i
) { i
->Notify(timer
); },
678 [&](const ObserverCallback
& o
) {
679 o
->Observe(timer
, NS_TIMER_CALLBACK_TOPIC
, nullptr);
681 [&](const FuncCallback
& f
) { f
.mFunc(timer
, f
.mClosure
); },
682 [&](const ClosureCallback
& c
) { c
.mFunc(timer
); });
684 TimeStamp now
= TimeStamp::Now();
686 MutexAutoLock
lock(mMutex
);
687 if (aGeneration
== mGeneration
) {
689 // Repeating timer has not been re-init or canceled; reschedule
691 mTimeout
= now
+ mDelay
;
694 // If we are late enough finishing the callback that we have missed
695 // some firings, do not attempt to play catchup, just get back on the
696 // cadence we're supposed to maintain.
697 unsigned missedFirings
=
698 static_cast<unsigned>((now
- mTimeout
) / mDelay
);
699 mTimeout
+= mDelay
* (missedFirings
+ 1);
701 // Can we stop allowing repeating timers with delay 0?
705 gThreadWrapper
.AddTimer(this, lock
);
707 // Non-repeating timer that has not been re-scheduled. Clear.
708 // XXX(nika): Other callsites seem to go to some effort to avoid
709 // destroying mCallback when it's held. Why not this one?
710 mCallback
= mozilla::AsVariant(UnknownCallback
{});
716 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
717 ("[this=%p] Took %fms to fire timer callback\n", this,
718 (now
- fireTime
).ToMilliseconds()));
721 // See the big comment above GetTimerFiringsLog() to understand this code.
722 void nsTimerImpl::LogFiring(const Callback
& aCallback
, uint8_t aType
,
726 case nsITimer::TYPE_ONE_SHOT
:
727 typeStr
= "ONE_SHOT ";
729 case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY
:
730 typeStr
= "ONE_LOW ";
732 case nsITimer::TYPE_REPEATING_SLACK
:
735 case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY
:
736 typeStr
= "SLACK_LOW ";
738 case nsITimer::TYPE_REPEATING_PRECISE
: /* fall through */
739 case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP
:
740 typeStr
= "PRECISE ";
743 MOZ_CRASH("bad type");
747 [&](const UnknownCallback
&) {
749 GetTimerFiringsLog(), LogLevel::Debug
,
750 ("[%d] ??? timer (%s, %5d ms)\n", getpid(), typeStr
, aDelay
));
752 [&](const InterfaceCallback
& i
) {
753 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
754 ("[%d] iface timer (%s %5d ms): %p\n", getpid(), typeStr
,
757 [&](const ObserverCallback
& o
) {
758 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
759 ("[%d] obs timer (%s %5d ms): %p\n", getpid(), typeStr
,
762 [&](const FuncCallback
& f
) {
763 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
764 ("[%d] fn timer (%s %5d ms): %s\n", getpid(), typeStr
,
767 [&](const ClosureCallback
& c
) {
768 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug
,
769 ("[%d] closure timer (%s %5d ms): %s\n", getpid(), typeStr
,
774 void nsTimerImpl::GetName(nsACString
& aName
,
775 const MutexAutoLock
& aProofOfLock
) {
777 [&](const UnknownCallback
&) { aName
.AssignLiteral("Canceled_timer"); },
778 [&](const InterfaceCallback
& i
) {
779 if (nsCOMPtr
<nsINamed
> named
= do_QueryInterface(i
)) {
780 named
->GetName(aName
);
782 aName
.AssignLiteral("Anonymous_interface_timer");
785 [&](const ObserverCallback
& o
) {
786 if (nsCOMPtr
<nsINamed
> named
= do_QueryInterface(o
)) {
787 named
->GetName(aName
);
789 aName
.AssignLiteral("Anonymous_observer_timer");
792 [&](const FuncCallback
& f
) { aName
.Assign(f
.mName
); },
793 [&](const ClosureCallback
& c
) { aName
.Assign(c
.mName
); });
796 nsresult
nsTimerImpl::GetName(nsACString
& aName
) {
797 MutexAutoLock
lock(mMutex
);
798 GetName(aName
, lock
);
802 nsTimer::~nsTimer() = default;
804 size_t nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) {
805 return aMallocSizeOf(this);
809 RefPtr
<nsTimer
> nsTimer::WithEventTarget(nsIEventTarget
* aTarget
) {
811 aTarget
= mozilla::GetCurrentSerialEventTarget();
813 return do_AddRef(new nsTimer(aTarget
));
817 nsresult
nsTimer::XPCOMConstructor(REFNSIID aIID
, void** aResult
) {
819 auto timer
= WithEventTarget(nullptr);
821 return timer
->QueryInterface(aIID
, aResult
);