Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / xpcom / threads / nsTimerImpl.cpp
blobb350e9c0a9be6ca25b819470be0970dfe0cc727a
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"
9 #include <utility>
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"
23 #include "pratom.h"
25 #ifdef XP_WIN
26 # include <process.h>
27 # ifndef getpid
28 # define getpid _getpid
29 # endif
30 #else
31 # include <unistd.h>
32 #endif
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
44 // the timer thread.
45 class TimerThreadWrapper {
46 public:
47 constexpr TimerThreadWrapper() : mThread(nullptr){};
48 ~TimerThreadWrapper() = default;
50 nsresult Init();
51 void Shutdown();
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);
62 private:
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();
73 NS_ADDREF(mThread);
75 return NS_OK;
78 void TimerThreadWrapper::Shutdown() {
79 RefPtr<TimerThread> thread;
82 mozilla::StaticMutexAutoLock lock(sMutex);
83 if (!mThread) {
84 return;
86 thread = mThread;
88 // Shutdown calls |nsTimerImpl::Cancel| which needs to make a call into
89 // |RemoveTimer|. This can't be done under the lock.
90 thread->Shutdown();
93 mozilla::StaticMutexAutoLock lock(sMutex);
94 NS_RELEASE(mThread);
98 nsresult TimerThreadWrapper::AddTimer(nsTimerImpl* aTimer,
99 const MutexAutoLock& aProofOfLock) {
100 mozilla::StaticMutexAutoLock lock(sMutex);
101 if (mThread) {
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);
110 if (mThread) {
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);
119 return mThread
120 ? mThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
121 : TimeStamp();
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);
133 if (!mThread) {
134 return NS_ERROR_NOT_AVAILABLE;
136 thread = mThread;
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,
151 aSearchBound);
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,
165 aType, aTarget));
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);
175 return NS_OK;
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,
183 aType, aTarget));
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);
193 return NS_OK;
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,
201 aType, aTarget));
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);
211 return NS_OK;
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));
220 return timer;
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));
238 return timer;
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,
248 aNameString));
249 timer.forget(aTimer);
250 return NS_OK;
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,
259 aTarget));
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,
270 aNameString));
271 timer.forget(aTimer);
272 return NS_OK;
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,
281 aTarget));
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);
294 return NS_OK;
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
311 // first.
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; }
320 #include <math.h>
322 /* static */
323 mozilla::StaticMutex nsTimerImpl::sDeltaMutex;
324 /* static */
325 double nsTimerImpl::sDeltaSumSquared MOZ_GUARDED_BY(nsTimerImpl::sDeltaMutex) =
327 /* static */
328 double nsTimerImpl::sDeltaSum MOZ_GUARDED_BY(nsTimerImpl::sDeltaMutex) = 0;
329 /* static */
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) {
340 var = 0.0;
341 } else {
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;
347 *meanResult = mean;
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");
365 if (count == 1) {
366 // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
367 mImpl->CancelImpl(true);
368 } else if (count == 0) {
369 delete this;
372 return count;
375 nsTimerImpl::nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget)
376 : mEventTarget(aTarget),
377 mIsInTimerThread(false),
378 mType(0),
379 mGeneration(0),
380 mITimer(aTimer),
381 mMutex("nsTimerImpl::mMutex"),
382 mCallback(UnknownCallback{}),
383 mFiring(0) {
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);
389 // static
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) {
411 if (!mEventTarget) {
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);
420 ++mGeneration;
422 mType = (uint8_t)aType;
423 mDelay = aDelay;
424 mTimeout = TimeStamp::Now() + mDelay;
426 return gThreadWrapper.AddTimer(this, aProofOfLock);
429 nsresult nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
430 void* aClosure, uint32_t aDelay,
431 uint32_t aType,
432 const char* aName) {
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,
470 uint32_t aType) {
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() {
496 CancelImpl(false);
497 return NS_OK;
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
509 // unlocked.
510 std::swap(cbTrash, mCallback);
511 ++mGeneration;
513 // Don't clear this if we're firing; once Fire returns, we'll get this call
514 // again.
515 if (aClearITimer && !mFiring) {
516 MOZ_RELEASE_ASSERT(
517 mITimer,
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.
530 NS_ERROR(
531 "nsITimer->SetDelay() called when the "
532 "one-shot timer is not set up.");
533 return NS_ERROR_NOT_INITIALIZED;
536 bool reAdd = false;
537 reAdd = NS_SUCCEEDED(gThreadWrapper.RemoveTimer(this, lock));
539 mDelay = TimeDuration::FromMilliseconds(aDelay);
540 mTimeout = TimeStamp::Now() + mDelay;
542 if (reAdd) {
543 gThreadWrapper.AddTimer(this, lock);
546 return NS_OK;
549 nsresult nsTimerImpl::GetDelay(uint32_t* aDelay) {
550 MutexAutoLock lock(mMutex);
551 *aDelay = mDelay.ToMilliseconds();
552 return NS_OK;
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.
561 return NS_OK;
564 nsresult nsTimerImpl::GetType(uint32_t* aType) {
565 MutexAutoLock lock(mMutex);
566 *aType = mType;
567 return NS_OK;
570 nsresult nsTimerImpl::GetClosure(void** aClosure) {
571 MutexAutoLock lock(mMutex);
572 if (GetCallback().is<FuncCallback>()) {
573 *aClosure = GetCallback().as<FuncCallback>().mClosure;
574 } else {
575 *aClosure = nullptr;
577 return NS_OK;
580 nsresult nsTimerImpl::GetCallback(nsITimerCallback** aCallback) {
581 MutexAutoLock lock(mMutex);
582 if (GetCallback().is<InterfaceCallback>()) {
583 NS_IF_ADDREF(*aCallback = GetCallback().as<InterfaceCallback>());
584 } else {
585 *aCallback = nullptr;
587 return NS_OK;
590 nsresult nsTimerImpl::GetTarget(nsIEventTarget** aTarget) {
591 MutexAutoLock lock(mMutex);
592 NS_IF_ADDREF(*aTarget = mEventTarget);
593 return NS_OK;
596 nsresult nsTimerImpl::SetTarget(nsIEventTarget* aTarget) {
597 MutexAutoLock lock(mMutex);
598 if (NS_WARN_IF(!mCallback.is<UnknownCallback>())) {
599 return NS_ERROR_ALREADY_INITIALIZED;
602 if (aTarget) {
603 mEventTarget = aTarget;
604 } else {
605 mEventTarget = mozilla::GetCurrentSerialEventTarget();
607 return NS_OK;
610 nsresult nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut) {
611 *aValueOut = gThreadWrapper.AllowedEarlyFiringMicroseconds();
612 return NS_OK;
615 void nsTimerImpl::Fire(int32_t aGeneration) {
616 uint8_t oldType;
617 uint32_t oldDelay;
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
628 // firing
629 return;
632 // We modify mTimeout, so we must not be in the current TimerThread's
633 // mTimers list.
634 MOZ_ASSERT(!mIsInTimerThread);
636 ++mFiring;
637 callbackDuringFire = mCallback;
638 oldType = mType;
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.
644 timer = mITimer;
647 AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER);
649 TimeStamp fireTime;
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);
656 sDeltaSum += abs(d);
657 sDeltaSumSquared += double(d) * double(d);
658 sDeltaNum++;
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) {
688 if (IsRepeating()) {
689 // Repeating timer has not been re-init or canceled; reschedule
690 if (IsSlack()) {
691 mTimeout = now + mDelay;
692 } else {
693 if (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);
700 } else {
701 // Can we stop allowing repeating timers with delay 0?
702 mTimeout = now;
705 gThreadWrapper.AddTimer(this, lock);
706 } else {
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{});
714 --mFiring;
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,
723 uint32_t aDelay) {
724 const char* typeStr;
725 switch (aType) {
726 case nsITimer::TYPE_ONE_SHOT:
727 typeStr = "ONE_SHOT ";
728 break;
729 case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY:
730 typeStr = "ONE_LOW ";
731 break;
732 case nsITimer::TYPE_REPEATING_SLACK:
733 typeStr = "SLACK ";
734 break;
735 case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY:
736 typeStr = "SLACK_LOW ";
737 break;
738 case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
739 case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP:
740 typeStr = "PRECISE ";
741 break;
742 default:
743 MOZ_CRASH("bad type");
746 aCallback.match(
747 [&](const UnknownCallback&) {
748 MOZ_LOG(
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,
755 aDelay, i.get()));
757 [&](const ObserverCallback& o) {
758 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
759 ("[%d] obs timer (%s %5d ms): %p\n", getpid(), typeStr,
760 aDelay, o.get()));
762 [&](const FuncCallback& f) {
763 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
764 ("[%d] fn timer (%s %5d ms): %s\n", getpid(), typeStr,
765 aDelay, f.mName));
767 [&](const ClosureCallback& c) {
768 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
769 ("[%d] closure timer (%s %5d ms): %s\n", getpid(), typeStr,
770 aDelay, c.mName));
774 void nsTimerImpl::GetName(nsACString& aName,
775 const MutexAutoLock& aProofOfLock) {
776 GetCallback().match(
777 [&](const UnknownCallback&) { aName.AssignLiteral("Canceled_timer"); },
778 [&](const InterfaceCallback& i) {
779 if (nsCOMPtr<nsINamed> named = do_QueryInterface(i)) {
780 named->GetName(aName);
781 } else {
782 aName.AssignLiteral("Anonymous_interface_timer");
785 [&](const ObserverCallback& o) {
786 if (nsCOMPtr<nsINamed> named = do_QueryInterface(o)) {
787 named->GetName(aName);
788 } else {
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);
799 return NS_OK;
802 nsTimer::~nsTimer() = default;
804 size_t nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
805 return aMallocSizeOf(this);
808 /* static */
809 RefPtr<nsTimer> nsTimer::WithEventTarget(nsIEventTarget* aTarget) {
810 if (!aTarget) {
811 aTarget = mozilla::GetCurrentSerialEventTarget();
813 return do_AddRef(new nsTimer(aTarget));
816 /* static */
817 nsresult nsTimer::XPCOMConstructor(REFNSIID aIID, void** aResult) {
818 *aResult = nullptr;
819 auto timer = WithEventTarget(nullptr);
821 return timer->QueryInterface(aIID, aResult);