Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / nsTimerImpl.cpp
blob4e6c93181b67fcad786d11f4b17306b74aaa4ec3
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 "nsThreadManager.h"
21 #include "nsThreadUtils.h"
22 #include "pratom.h"
24 #ifdef XP_WIN
25 # include <process.h>
26 # ifndef getpid
27 # define getpid _getpid
28 # endif
29 #else
30 # include <unistd.h>
31 #endif
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
43 // the timer thread.
44 class TimerThreadWrapper {
45 public:
46 constexpr TimerThreadWrapper() : mThread(nullptr){};
47 ~TimerThreadWrapper() = default;
49 nsresult Init();
50 void Shutdown();
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();
58 private:
59 static mozilla::StaticMutex sMutex;
60 TimerThread* mThread;
63 mozilla::StaticMutex TimerThreadWrapper::sMutex;
65 nsresult TimerThreadWrapper::Init() {
66 nsresult rv;
67 mozilla::StaticMutexAutoLock lock(sMutex);
68 mThread = new TimerThread();
70 NS_ADDREF(mThread);
71 rv = mThread->InitLocks();
73 if (NS_FAILED(rv)) {
74 NS_RELEASE(mThread);
77 return rv;
80 void TimerThreadWrapper::Shutdown() {
81 RefPtr<TimerThread> thread;
84 mozilla::StaticMutexAutoLock lock(sMutex);
85 if (!mThread) {
86 return;
88 thread = mThread;
90 // Shutdown calls |nsTimerImpl::Cancel| which needs to make a call into
91 // |RemoveTimer|. This can't be done under the lock.
92 thread->Shutdown();
95 mozilla::StaticMutexAutoLock lock(sMutex);
96 NS_RELEASE(mThread);
100 nsresult TimerThreadWrapper::AddTimer(nsTimerImpl* aTimer,
101 const MutexAutoLock& aProofOfLock) {
102 mozilla::StaticMutexAutoLock lock(sMutex);
103 if (mThread) {
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);
112 if (mThread) {
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);
121 return mThread
122 ? mThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
123 : TimeStamp();
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,
141 aSearchBound);
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,
155 aType, aTarget));
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);
165 return NS_OK;
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,
173 aType, aTarget));
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);
183 return NS_OK;
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,
191 aType, aTarget));
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);
201 return NS_OK;
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));
210 return timer;
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));
228 return timer;
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,
238 aNameString));
239 timer.forget(aTimer);
240 return NS_OK;
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,
249 aTarget));
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,
260 aNameString));
261 timer.forget(aTimer);
262 return NS_OK;
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,
271 aTarget));
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);
284 return NS_OK;
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
301 // first.
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; }
310 #include <math.h>
312 /* static */
313 mozilla::StaticMutex nsTimerImpl::sDeltaMutex;
314 /* static */
315 double nsTimerImpl::sDeltaSumSquared = 0;
316 /* static */
317 double nsTimerImpl::sDeltaSum = 0;
318 /* static */
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) {
329 var = 0.0;
330 } else {
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;
336 *meanResult = mean;
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");
348 if (count == 1) {
349 // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
350 mImpl->CancelImpl(true);
351 } else if (count == 0) {
352 delete this;
355 return count;
358 nsTimerImpl::nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget)
359 : mEventTarget(aTarget),
360 mHolder(nullptr),
361 mType(0),
362 mGeneration(0),
363 mITimer(aTimer),
364 mMutex("nsTimerImpl::mMutex"),
365 mCallback(UnknownCallback{}),
366 mFiring(0) {
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);
372 // static
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) {
394 if (!mEventTarget) {
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);
404 ++mGeneration;
406 mType = (uint8_t)aType;
407 mDelay = aDelay;
408 mTimeout = TimeStamp::Now() + mDelay;
410 return gThreadWrapper.AddTimer(this, aProofOfLock);
413 nsresult nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
414 void* aClosure, uint32_t aDelay,
415 uint32_t aType,
416 const char* aName) {
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,
454 uint32_t aType) {
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() {
480 CancelImpl(false);
481 return NS_OK;
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
493 // unlocked.
494 std::swap(cbTrash, mCallback);
495 ++mGeneration;
497 // Don't clear this if we're firing; once Fire returns, we'll get this call
498 // again.
499 if (aClearITimer && !mFiring) {
500 MOZ_RELEASE_ASSERT(
501 mITimer,
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.
514 NS_ERROR(
515 "nsITimer->SetDelay() called when the "
516 "one-shot timer is not set up.");
517 return NS_ERROR_NOT_INITIALIZED;
520 bool reAdd = false;
521 reAdd = NS_SUCCEEDED(gThreadWrapper.RemoveTimer(this, lock));
523 mDelay = TimeDuration::FromMilliseconds(aDelay);
524 mTimeout = TimeStamp::Now() + mDelay;
526 if (reAdd) {
527 gThreadWrapper.AddTimer(this, lock);
530 return NS_OK;
533 nsresult nsTimerImpl::GetDelay(uint32_t* aDelay) {
534 MutexAutoLock lock(mMutex);
535 *aDelay = mDelay.ToMilliseconds();
536 return NS_OK;
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.
545 return NS_OK;
548 nsresult nsTimerImpl::GetType(uint32_t* aType) {
549 MutexAutoLock lock(mMutex);
550 *aType = mType;
551 return NS_OK;
554 nsresult nsTimerImpl::GetClosure(void** aClosure) {
555 MutexAutoLock lock(mMutex);
556 if (GetCallback().is<FuncCallback>()) {
557 *aClosure = GetCallback().as<FuncCallback>().mClosure;
558 } else {
559 *aClosure = nullptr;
561 return NS_OK;
564 nsresult nsTimerImpl::GetCallback(nsITimerCallback** aCallback) {
565 MutexAutoLock lock(mMutex);
566 if (GetCallback().is<InterfaceCallback>()) {
567 NS_IF_ADDREF(*aCallback = GetCallback().as<InterfaceCallback>());
568 } else {
569 *aCallback = nullptr;
571 return NS_OK;
574 nsresult nsTimerImpl::GetTarget(nsIEventTarget** aTarget) {
575 MutexAutoLock lock(mMutex);
576 NS_IF_ADDREF(*aTarget = mEventTarget);
577 return NS_OK;
580 nsresult nsTimerImpl::SetTarget(nsIEventTarget* aTarget) {
581 MutexAutoLock lock(mMutex);
582 if (NS_WARN_IF(!mCallback.is<UnknownCallback>())) {
583 return NS_ERROR_ALREADY_INITIALIZED;
586 if (aTarget) {
587 mEventTarget = aTarget;
588 } else {
589 mEventTarget = mozilla::GetCurrentSerialEventTarget();
591 return NS_OK;
594 nsresult nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut) {
595 *aValueOut = gThreadWrapper.AllowedEarlyFiringMicroseconds();
596 return NS_OK;
599 void nsTimerImpl::Fire(int32_t aGeneration) {
600 uint8_t oldType;
601 uint32_t oldDelay;
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
612 // firing
613 return;
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);
621 ++mFiring;
622 callbackDuringFire = mCallback;
623 oldType = mType;
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.
629 timer = mITimer;
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);
640 sDeltaSum += abs(d);
641 sDeltaSumSquared += double(d) * double(d);
642 sDeltaNum++;
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) {
672 if (IsRepeating()) {
673 // Repeating timer has not been re-init or canceled; reschedule
674 if (IsSlack()) {
675 mTimeout = now + mDelay;
676 } else {
677 if (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);
684 } else {
685 // Can we stop allowing repeating timers with delay 0?
686 mTimeout = now;
689 gThreadWrapper.AddTimer(this, lock);
690 } else {
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{});
698 --mFiring;
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,
707 uint32_t aDelay) {
708 const char* typeStr;
709 switch (aType) {
710 case nsITimer::TYPE_ONE_SHOT:
711 typeStr = "ONE_SHOT ";
712 break;
713 case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY:
714 typeStr = "ONE_LOW ";
715 break;
716 case nsITimer::TYPE_REPEATING_SLACK:
717 typeStr = "SLACK ";
718 break;
719 case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY:
720 typeStr = "SLACK_LOW ";
721 break;
722 case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
723 case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP:
724 typeStr = "PRECISE ";
725 break;
726 default:
727 MOZ_CRASH("bad type");
730 aCallback.match(
731 [&](const UnknownCallback&) {
732 MOZ_LOG(
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,
739 aDelay, i.get()));
741 [&](const ObserverCallback& o) {
742 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
743 ("[%d] obs timer (%s %5d ms): %p\n", getpid(), typeStr,
744 aDelay, o.get()));
746 [&](const FuncCallback& f) {
747 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
748 ("[%d] fn timer (%s %5d ms): %s\n", getpid(), typeStr,
749 aDelay, f.mName));
751 [&](const ClosureCallback& c) {
752 MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
753 ("[%d] closure timer (%s %5d ms): %s\n", getpid(), typeStr,
754 aDelay, c.mName));
758 void nsTimerImpl::GetName(nsACString& aName,
759 const MutexAutoLock& aProofOfLock) {
760 GetCallback().match(
761 [&](const UnknownCallback&) { aName.AssignLiteral("Canceled_timer"); },
762 [&](const InterfaceCallback& i) {
763 if (nsCOMPtr<nsINamed> named = do_QueryInterface(i)) {
764 named->GetName(aName);
765 } else {
766 aName.AssignLiteral("Anonymous_interface_timer");
769 [&](const ObserverCallback& o) {
770 if (nsCOMPtr<nsINamed> named = do_QueryInterface(o)) {
771 named->GetName(aName);
772 } else {
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);
793 /* static */
794 RefPtr<nsTimer> nsTimer::WithEventTarget(nsIEventTarget* aTarget) {
795 if (!aTarget) {
796 aTarget = mozilla::GetCurrentSerialEventTarget();
798 return do_AddRef(new nsTimer(aTarget));
801 /* static */
802 nsresult nsTimer::XPCOMConstructor(nsISupports* aOuter, REFNSIID aIID,
803 void** aResult) {
804 *aResult = nullptr;
805 if (aOuter != nullptr) {
806 return NS_ERROR_NO_AGGREGATION;
809 auto timer = WithEventTarget(nullptr);
811 return timer->QueryInterface(aIID, aResult);