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/. */
6 
7 #include "nsTimerImpl.h"
8 
9 #include <utility>
10 
11 #include "GeckoProfiler.h"
12 #include "TimerThread.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/IntegerPrintfMacros.h"
15 #include "mozilla/Logging.h"
16 #include "mozilla/Mutex.h"
17 #include "mozilla/ResultExtensions.h"
18 #include "nsThreadManager.h"
19 #include "nsThreadUtils.h"
20 #include "pratom.h"
21 #ifdef MOZ_TASK_TRACER
22 #  include "GeckoTaskTracerImpl.h"
23 using namespace mozilla::tasktracer;
24 #endif
25 
26 #ifdef XP_WIN
27 #  include <process.h>
28 #  ifndef getpid
29 #    define getpid _getpid
30 #  endif
31 #else
32 #  include <unistd.h>
33 #endif
34 
35 using mozilla::Atomic;
36 using mozilla::LogLevel;
37 using mozilla::MakeRefPtr;
38 using mozilla::MutexAutoLock;
39 using mozilla::TimeDuration;
40 using mozilla::TimeStamp;
41 
42 static TimerThread* gThread = nullptr;
43 
44 // This module prints info about the precision of timers.
45 static mozilla::LazyLogModule sTimerLog("nsTimerImpl");
46 
GetTimerLog()47 mozilla::LogModule* GetTimerLog() { return sTimerLog; }
48 
NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault,uint32_t aSearchBound)49 TimeStamp NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault,
50                                                  uint32_t aSearchBound) {
51   return gThread
52              ? gThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
53              : TimeStamp();
54 }
55 
NS_NewTimer()56 already_AddRefed<nsITimer> NS_NewTimer() { return NS_NewTimer(nullptr); }
57 
NS_NewTimer(nsIEventTarget * aTarget)58 already_AddRefed<nsITimer> NS_NewTimer(nsIEventTarget* aTarget) {
59   return nsTimer::WithEventTarget(aTarget).forget();
60 }
61 
NS_NewTimerWithObserver(nsIObserver * aObserver,uint32_t aDelay,uint32_t aType,nsIEventTarget * aTarget)62 mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithObserver(
63     nsIObserver* aObserver, uint32_t aDelay, uint32_t aType,
64     nsIEventTarget* aTarget) {
65   nsCOMPtr<nsITimer> timer;
66   MOZ_TRY(NS_NewTimerWithObserver(getter_AddRefs(timer), aObserver, aDelay,
67                                   aType, aTarget));
68   return std::move(timer);
69 }
NS_NewTimerWithObserver(nsITimer ** aTimer,nsIObserver * aObserver,uint32_t aDelay,uint32_t aType,nsIEventTarget * aTarget)70 nsresult NS_NewTimerWithObserver(nsITimer** aTimer, nsIObserver* aObserver,
71                                  uint32_t aDelay, uint32_t aType,
72                                  nsIEventTarget* aTarget) {
73   auto timer = nsTimer::WithEventTarget(aTarget);
74 
75   MOZ_TRY(timer->Init(aObserver, aDelay, aType));
76   timer.forget(aTimer);
77   return NS_OK;
78 }
79 
NS_NewTimerWithCallback(nsITimerCallback * aCallback,uint32_t aDelay,uint32_t aType,nsIEventTarget * aTarget)80 mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithCallback(
81     nsITimerCallback* aCallback, uint32_t aDelay, uint32_t aType,
82     nsIEventTarget* aTarget) {
83   nsCOMPtr<nsITimer> timer;
84   MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer), aCallback, aDelay,
85                                   aType, aTarget));
86   return std::move(timer);
87 }
NS_NewTimerWithCallback(nsITimer ** aTimer,nsITimerCallback * aCallback,uint32_t aDelay,uint32_t aType,nsIEventTarget * aTarget)88 nsresult NS_NewTimerWithCallback(nsITimer** aTimer, nsITimerCallback* aCallback,
89                                  uint32_t aDelay, uint32_t aType,
90                                  nsIEventTarget* aTarget) {
91   auto timer = nsTimer::WithEventTarget(aTarget);
92 
93   MOZ_TRY(timer->InitWithCallback(aCallback, aDelay, aType));
94   timer.forget(aTimer);
95   return NS_OK;
96 }
97 
NS_NewTimerWithCallback(nsITimerCallback * aCallback,const TimeDuration & aDelay,uint32_t aType,nsIEventTarget * aTarget)98 mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithCallback(
99     nsITimerCallback* aCallback, const TimeDuration& aDelay, uint32_t aType,
100     nsIEventTarget* aTarget) {
101   nsCOMPtr<nsITimer> timer;
102   MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer), aCallback, aDelay,
103                                   aType, aTarget));
104   return std::move(timer);
105 }
NS_NewTimerWithCallback(nsITimer ** aTimer,nsITimerCallback * aCallback,const TimeDuration & aDelay,uint32_t aType,nsIEventTarget * aTarget)106 nsresult NS_NewTimerWithCallback(nsITimer** aTimer, nsITimerCallback* aCallback,
107                                  const TimeDuration& aDelay, uint32_t aType,
108                                  nsIEventTarget* aTarget) {
109   auto timer = nsTimer::WithEventTarget(aTarget);
110 
111   MOZ_TRY(timer->InitHighResolutionWithCallback(aCallback, aDelay, aType));
112   timer.forget(aTimer);
113   return NS_OK;
114 }
115 
NS_NewTimerWithFuncCallback(nsTimerCallbackFunc aCallback,void * aClosure,uint32_t aDelay,uint32_t aType,const char * aNameString,nsIEventTarget * aTarget)116 mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithFuncCallback(
117     nsTimerCallbackFunc aCallback, void* aClosure, uint32_t aDelay,
118     uint32_t aType, const char* aNameString, nsIEventTarget* aTarget) {
119   nsCOMPtr<nsITimer> timer;
120   MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback,
121                                       aClosure, aDelay, aType, aNameString,
122                                       aTarget));
123   return std::move(timer);
124 }
NS_NewTimerWithFuncCallback(nsITimer ** aTimer,nsTimerCallbackFunc aCallback,void * aClosure,uint32_t aDelay,uint32_t aType,const char * aNameString,nsIEventTarget * aTarget)125 nsresult NS_NewTimerWithFuncCallback(nsITimer** aTimer,
126                                      nsTimerCallbackFunc aCallback,
127                                      void* aClosure, uint32_t aDelay,
128                                      uint32_t aType, const char* aNameString,
129                                      nsIEventTarget* aTarget) {
130   auto timer = nsTimer::WithEventTarget(aTarget);
131 
132   MOZ_TRY(timer->InitWithNamedFuncCallback(aCallback, aClosure, aDelay, aType,
133                                            aNameString));
134   timer.forget(aTimer);
135   return NS_OK;
136 }
137 
NS_NewTimerWithFuncCallback(nsTimerCallbackFunc aCallback,void * aClosure,uint32_t aDelay,uint32_t aType,nsTimerNameCallbackFunc aNameCallback,nsIEventTarget * aTarget)138 mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithFuncCallback(
139     nsTimerCallbackFunc aCallback, void* aClosure, uint32_t aDelay,
140     uint32_t aType, nsTimerNameCallbackFunc aNameCallback,
141     nsIEventTarget* aTarget) {
142   nsCOMPtr<nsITimer> timer;
143   MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback,
144                                       aClosure, aDelay, aType, aNameCallback,
145                                       aTarget));
146   return std::move(timer);
147 }
NS_NewTimerWithFuncCallback(nsITimer ** aTimer,nsTimerCallbackFunc aCallback,void * aClosure,uint32_t aDelay,uint32_t aType,nsTimerNameCallbackFunc aNameCallback,nsIEventTarget * aTarget)148 nsresult NS_NewTimerWithFuncCallback(nsITimer** aTimer,
149                                      nsTimerCallbackFunc aCallback,
150                                      void* aClosure, uint32_t aDelay,
151                                      uint32_t aType,
152                                      nsTimerNameCallbackFunc aNameCallback,
153                                      nsIEventTarget* aTarget) {
154   auto timer = nsTimer::WithEventTarget(aTarget);
155 
156   MOZ_TRY(timer->InitWithNameableFuncCallback(aCallback, aClosure, aDelay,
157                                               aType, aNameCallback));
158   timer.forget(aTimer);
159   return NS_OK;
160 }
161 
162 // This module prints info about which timers are firing, which is useful for
163 // wakeups for the purposes of power profiling. Set the following environment
164 // variable before starting the browser.
165 //
166 //   MOZ_LOG=TimerFirings:4
167 //
168 // Then a line will be printed for every timer that fires. The name used for a
169 // |Callback::Type::Function| timer depends on the circumstances.
170 //
171 // - If it was explicitly named (e.g. it was initialized with
172 //   InitWithNamedFuncCallback()) then that explicit name will be shown.
173 //
174 // - Otherwise, if we are on a platform that supports function name lookup
175 //   (Mac or Linux) then the looked-up name will be shown with a
176 //   "[from dladdr]" annotation. On Mac the looked-up name will be immediately
177 //   useful. On Linux it'll need post-processing with `tools/rb/fix_stacks.py`.
178 //
179 // - Otherwise, no name will be printed. If many timers hit this case then
180 //   you'll need to re-run the workload on a Mac to find out which timers they
181 //   are, and then give them explicit names.
182 //
183 // If you redirect this output to a file called "out", you can then
184 // post-process it with a command something like the following.
185 //
186 //   cat out | grep timer | sort | uniq -c | sort -r -n
187 //
188 // This will show how often each unique line appears, with the most common ones
189 // first.
190 //
191 // More detailed docs are here:
192 // https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
193 //
194 static mozilla::LazyLogModule sTimerFiringsLog("TimerFirings");
195 
GetTimerFiringsLog()196 static mozilla::LogModule* GetTimerFiringsLog() { return sTimerFiringsLog; }
197 
198 #include <math.h>
199 
200 double nsTimerImpl::sDeltaSumSquared = 0;
201 double nsTimerImpl::sDeltaSum = 0;
202 double nsTimerImpl::sDeltaNum = 0;
203 
myNS_MeanAndStdDev(double n,double sumOfValues,double sumOfSquaredValues,double * meanResult,double * stdDevResult)204 static void myNS_MeanAndStdDev(double n, double sumOfValues,
205                                double sumOfSquaredValues, double* meanResult,
206                                double* stdDevResult) {
207   double mean = 0.0, var = 0.0, stdDev = 0.0;
208   if (n > 0.0 && sumOfValues >= 0) {
209     mean = sumOfValues / n;
210     double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
211     if (temp < 0.0 || n <= 1) {
212       var = 0.0;
213     } else {
214       var = temp / (n * (n - 1));
215     }
216     // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
217     stdDev = var != 0.0 ? sqrt(var) : 0.0;
218   }
219   *meanResult = mean;
220   *stdDevResult = stdDev;
221 }
222 
NS_IMPL_QUERY_INTERFACE(nsTimer,nsITimer)223 NS_IMPL_QUERY_INTERFACE(nsTimer, nsITimer)
224 NS_IMPL_ADDREF(nsTimer)
225 
226 NS_IMETHODIMP_(MozExternalRefCountType)
227 nsTimer::Release(void) {
228   nsrefcnt count = --mRefCnt;
229   NS_LOG_RELEASE(this, count, "nsTimer");
230 
231   if (count == 1) {
232     // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
233     mImpl->CancelImpl(true);
234   } else if (count == 0) {
235     delete this;
236   }
237 
238   return count;
239 }
240 
nsTimerImpl(nsITimer * aTimer,nsIEventTarget * aTarget)241 nsTimerImpl::nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget)
242     : mEventTarget(aTarget),
243       mHolder(nullptr),
244       mType(0),
245       mGeneration(0),
246       mITimer(aTimer),
247       mMutex("nsTimerImpl::mMutex"),
248       mFiring(0) {
249   // XXX some code creates timers during xpcom shutdown, when threads are no
250   // longer available, so we cannot turn this on yet.
251   // MOZ_ASSERT(mEventTarget);
252 }
253 
254 // static
Startup()255 nsresult nsTimerImpl::Startup() {
256   nsresult rv;
257 
258   gThread = new TimerThread();
259 
260   NS_ADDREF(gThread);
261   rv = gThread->InitLocks();
262 
263   if (NS_FAILED(rv)) {
264     NS_RELEASE(gThread);
265   }
266 
267   return rv;
268 }
269 
Shutdown()270 void nsTimerImpl::Shutdown() {
271   if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
272     double mean = 0, stddev = 0;
273     myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
274 
275     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
276             ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
277              sDeltaNum, sDeltaSum, sDeltaSumSquared));
278     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
279             ("mean: %fms, stddev: %fms\n", mean, stddev));
280   }
281 
282   if (!gThread) {
283     return;
284   }
285 
286   gThread->Shutdown();
287   NS_RELEASE(gThread);
288 }
289 
InitCommon(uint32_t aDelayMS,uint32_t aType,Callback && aNewCallback)290 nsresult nsTimerImpl::InitCommon(uint32_t aDelayMS, uint32_t aType,
291                                  Callback&& aNewCallback) {
292   return InitCommon(TimeDuration::FromMilliseconds(aDelayMS), aType,
293                     std::move(aNewCallback));
294 }
295 
InitCommon(const TimeDuration & aDelay,uint32_t aType,Callback && newCallback)296 nsresult nsTimerImpl::InitCommon(const TimeDuration& aDelay, uint32_t aType,
297                                  Callback&& newCallback) {
298   mMutex.AssertCurrentThreadOwns();
299 
300   if (!gThread) {
301     return NS_ERROR_NOT_INITIALIZED;
302   }
303 
304   if (!mEventTarget) {
305     NS_ERROR("mEventTarget is NULL");
306     return NS_ERROR_NOT_INITIALIZED;
307   }
308 
309   gThread->RemoveTimer(this);
310   mCallback.swap(newCallback);
311   ++mGeneration;
312 
313   mType = (uint8_t)aType;
314   mDelay = aDelay;
315   mTimeout = TimeStamp::Now() + mDelay;
316 
317   return gThread->AddTimer(this);
318 }
319 
InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,void * aClosure,uint32_t aDelay,uint32_t aType,const Callback::Name & aName)320 nsresult nsTimerImpl::InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
321                                                  void* aClosure,
322                                                  uint32_t aDelay,
323                                                  uint32_t aType,
324                                                  const Callback::Name& aName) {
325   if (NS_WARN_IF(!aFunc)) {
326     return NS_ERROR_INVALID_ARG;
327   }
328 
329   Callback cb;  // Goes out of scope after the unlock, prevents deadlock
330   cb.mType = Callback::Type::Function;
331   cb.mCallback.c = aFunc;
332   cb.mClosure = aClosure;
333   cb.mName = aName;
334 
335   MutexAutoLock lock(mMutex);
336   return InitCommon(aDelay, aType, std::move(cb));
337 }
338 
InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,void * aClosure,uint32_t aDelay,uint32_t aType,const char * aNameString)339 nsresult nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
340                                                 void* aClosure, uint32_t aDelay,
341                                                 uint32_t aType,
342                                                 const char* aNameString) {
343   Callback::Name name(aNameString);
344   return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
345 }
346 
InitWithNameableFuncCallback(nsTimerCallbackFunc aFunc,void * aClosure,uint32_t aDelay,uint32_t aType,nsTimerNameCallbackFunc aNameFunc)347 nsresult nsTimerImpl::InitWithNameableFuncCallback(
348     nsTimerCallbackFunc aFunc, void* aClosure, uint32_t aDelay, uint32_t aType,
349     nsTimerNameCallbackFunc aNameFunc) {
350   Callback::Name name(aNameFunc);
351   return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
352 }
353 
InitWithCallback(nsITimerCallback * aCallback,uint32_t aDelayInMs,uint32_t aType)354 nsresult nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
355                                        uint32_t aDelayInMs, uint32_t aType) {
356   return InitHighResolutionWithCallback(
357       aCallback, TimeDuration::FromMilliseconds(aDelayInMs), aType);
358 }
359 
InitHighResolutionWithCallback(nsITimerCallback * aCallback,const TimeDuration & aDelay,uint32_t aType)360 nsresult nsTimerImpl::InitHighResolutionWithCallback(
361     nsITimerCallback* aCallback, const TimeDuration& aDelay, uint32_t aType) {
362   if (NS_WARN_IF(!aCallback)) {
363     return NS_ERROR_INVALID_ARG;
364   }
365 
366   Callback cb;  // Goes out of scope after the unlock, prevents deadlock
367   cb.mType = Callback::Type::Interface;
368   cb.mCallback.i = aCallback;
369   NS_ADDREF(cb.mCallback.i);
370 
371   MutexAutoLock lock(mMutex);
372   return InitCommon(aDelay, aType, std::move(cb));
373 }
374 
Init(nsIObserver * aObserver,uint32_t aDelayInMs,uint32_t aType)375 nsresult nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelayInMs,
376                            uint32_t aType) {
377   if (NS_WARN_IF(!aObserver)) {
378     return NS_ERROR_INVALID_ARG;
379   }
380 
381   Callback cb;  // Goes out of scope after the unlock, prevents deadlock
382   cb.mType = Callback::Type::Observer;
383   cb.mCallback.o = aObserver;
384   NS_ADDREF(cb.mCallback.o);
385 
386   MutexAutoLock lock(mMutex);
387   return InitCommon(aDelayInMs, aType, std::move(cb));
388 }
389 
Cancel()390 nsresult nsTimerImpl::Cancel() {
391   CancelImpl(false);
392   return NS_OK;
393 }
394 
CancelImpl(bool aClearITimer)395 void nsTimerImpl::CancelImpl(bool aClearITimer) {
396   Callback cbTrash;
397   RefPtr<nsITimer> timerTrash;
398 
399   {
400     MutexAutoLock lock(mMutex);
401     if (gThread) {
402       gThread->RemoveTimer(this);
403     }
404 
405     cbTrash.swap(mCallback);
406     ++mGeneration;
407 
408     // Don't clear this if we're firing; once Fire returns, we'll get this call
409     // again.
410     if (aClearITimer && !mFiring) {
411       MOZ_RELEASE_ASSERT(
412           mITimer,
413           "mITimer was nulled already! "
414           "This indicates that someone has messed up the refcount on nsTimer!");
415       timerTrash.swap(mITimer);
416     }
417   }
418 }
419 
SetDelay(uint32_t aDelay)420 nsresult nsTimerImpl::SetDelay(uint32_t aDelay) {
421   MutexAutoLock lock(mMutex);
422   if (GetCallback().mType == Callback::Type::Unknown && !IsRepeating()) {
423     // This may happen if someone tries to re-use a one-shot timer
424     // by re-setting delay instead of reinitializing the timer.
425     NS_ERROR(
426         "nsITimer->SetDelay() called when the "
427         "one-shot timer is not set up.");
428     return NS_ERROR_NOT_INITIALIZED;
429   }
430 
431   bool reAdd = false;
432   if (gThread) {
433     reAdd = NS_SUCCEEDED(gThread->RemoveTimer(this));
434   }
435 
436   mDelay = TimeDuration::FromMilliseconds(aDelay);
437   mTimeout = TimeStamp::Now() + mDelay;
438 
439   if (reAdd) {
440     gThread->AddTimer(this);
441   }
442 
443   return NS_OK;
444 }
445 
GetDelay(uint32_t * aDelay)446 nsresult nsTimerImpl::GetDelay(uint32_t* aDelay) {
447   MutexAutoLock lock(mMutex);
448   *aDelay = mDelay.ToMilliseconds();
449   return NS_OK;
450 }
451 
SetType(uint32_t aType)452 nsresult nsTimerImpl::SetType(uint32_t aType) {
453   MutexAutoLock lock(mMutex);
454   mType = (uint8_t)aType;
455   // XXX if this is called, we should change the actual type.. this could effect
456   // repeating timers.  we need to ensure in Fire() that if mType has changed
457   // during the callback that we don't end up with the timer in the queue twice.
458   return NS_OK;
459 }
460 
GetType(uint32_t * aType)461 nsresult nsTimerImpl::GetType(uint32_t* aType) {
462   MutexAutoLock lock(mMutex);
463   *aType = mType;
464   return NS_OK;
465 }
466 
GetClosure(void ** aClosure)467 nsresult nsTimerImpl::GetClosure(void** aClosure) {
468   MutexAutoLock lock(mMutex);
469   *aClosure = GetCallback().mClosure;
470   return NS_OK;
471 }
472 
GetCallback(nsITimerCallback ** aCallback)473 nsresult nsTimerImpl::GetCallback(nsITimerCallback** aCallback) {
474   MutexAutoLock lock(mMutex);
475   if (GetCallback().mType == Callback::Type::Interface) {
476     NS_IF_ADDREF(*aCallback = GetCallback().mCallback.i);
477   } else {
478     *aCallback = nullptr;
479   }
480 
481   return NS_OK;
482 }
483 
GetTarget(nsIEventTarget ** aTarget)484 nsresult nsTimerImpl::GetTarget(nsIEventTarget** aTarget) {
485   MutexAutoLock lock(mMutex);
486   NS_IF_ADDREF(*aTarget = mEventTarget);
487   return NS_OK;
488 }
489 
SetTarget(nsIEventTarget * aTarget)490 nsresult nsTimerImpl::SetTarget(nsIEventTarget* aTarget) {
491   MutexAutoLock lock(mMutex);
492   if (NS_WARN_IF(mCallback.mType != Callback::Type::Unknown)) {
493     return NS_ERROR_ALREADY_INITIALIZED;
494   }
495 
496   if (aTarget) {
497     mEventTarget = aTarget;
498   } else {
499     mEventTarget = mozilla::GetCurrentThreadEventTarget();
500   }
501   return NS_OK;
502 }
503 
GetAllowedEarlyFiringMicroseconds(uint32_t * aValueOut)504 nsresult nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut) {
505   *aValueOut = gThread ? gThread->AllowedEarlyFiringMicroseconds() : 0;
506   return NS_OK;
507 }
508 
Fire(int32_t aGeneration)509 void nsTimerImpl::Fire(int32_t aGeneration) {
510   uint8_t oldType;
511   uint32_t oldDelay;
512   TimeStamp oldTimeout;
513   Callback callbackDuringFire;
514   nsCOMPtr<nsITimer> kungFuDeathGrip;
515 
516   {
517     // Don't fire callbacks or fiddle with refcounts when the mutex is locked.
518     // If some other thread Cancels/Inits after this, they're just too late.
519     MutexAutoLock lock(mMutex);
520     if (aGeneration != mGeneration) {
521       return;
522     }
523 
524     ++mFiring;
525     callbackDuringFire = mCallback;
526     oldType = mType;
527     oldDelay = mDelay.ToMilliseconds();
528     oldTimeout = mTimeout;
529     // Ensure that the nsITimer does not unhook from the nsTimerImpl during
530     // Fire; this will cause null pointer crashes if the user of the timer drops
531     // its reference, and then uses the nsITimer* passed in the callback.
532     kungFuDeathGrip = mITimer;
533   }
534 
535   AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER);
536 
537   TimeStamp now = TimeStamp::Now();
538   if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
539     TimeDuration delta = now - oldTimeout;
540     int32_t d = delta.ToMilliseconds();  // delta in ms
541     sDeltaSum += abs(d);
542     sDeltaSumSquared += double(d) * double(d);
543     sDeltaNum++;
544 
545     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
546             ("[this=%p] expected delay time %4ums\n", this, oldDelay));
547     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
548             ("[this=%p] actual delay time   %4dms\n", this, oldDelay + d));
549     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
550             ("[this=%p] (mType is %d)       -------\n", this, oldType));
551     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
552             ("[this=%p]     delta           %4dms\n", this, d));
553   }
554 
555   if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug)) {
556     LogFiring(callbackDuringFire, oldType, oldDelay);
557   }
558 
559   switch (callbackDuringFire.mType) {
560     case Callback::Type::Function:
561       callbackDuringFire.mCallback.c(mITimer, callbackDuringFire.mClosure);
562       break;
563     case Callback::Type::Interface:
564       callbackDuringFire.mCallback.i->Notify(mITimer);
565       break;
566     case Callback::Type::Observer:
567       callbackDuringFire.mCallback.o->Observe(mITimer, NS_TIMER_CALLBACK_TOPIC,
568                                               nullptr);
569       break;
570     default:;
571   }
572 
573   MutexAutoLock lock(mMutex);
574   if (aGeneration == mGeneration) {
575     if (IsRepeating()) {
576       // Repeating timer has not been re-init or canceled; reschedule
577       if (IsSlack()) {
578         mTimeout = TimeStamp::Now() + mDelay;
579       } else {
580         mTimeout = mTimeout + mDelay;
581       }
582       if (gThread) {
583         gThread->AddTimer(this);
584       }
585     } else {
586       // Non-repeating timer that has not been re-scheduled. Clear.
587       mCallback.clear();
588     }
589   }
590 
591   --mFiring;
592 
593   MOZ_LOG(GetTimerLog(), LogLevel::Debug,
594           ("[this=%p] Took %fms to fire timer callback\n", this,
595            (TimeStamp::Now() - now).ToMilliseconds()));
596 }
597 
598 #if defined(HAVE_DLADDR) && defined(HAVE___CXA_DEMANGLE)
599 #  define USE_DLADDR 1
600 #endif
601 
602 #ifdef USE_DLADDR
603 #  include <cxxabi.h>
604 #  include <dlfcn.h>
605 #endif
606 
607 // See the big comment above GetTimerFiringsLog() to understand this code.
LogFiring(const Callback & aCallback,uint8_t aType,uint32_t aDelay)608 void nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType,
609                             uint32_t aDelay) {
610   const char* typeStr;
611   switch (aType) {
612     case nsITimer::TYPE_ONE_SHOT:
613       typeStr = "ONE_SHOT  ";
614       break;
615     case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY:
616       typeStr = "ONE_LOW   ";
617       break;
618     case nsITimer::TYPE_REPEATING_SLACK:
619       typeStr = "SLACK     ";
620       break;
621     case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY:
622       typeStr = "SLACK_LOW ";
623       break;
624     case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
625     case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP:
626       typeStr = "PRECISE   ";
627       break;
628     default:
629       MOZ_CRASH("bad type");
630   }
631 
632   switch (aCallback.mType) {
633     case Callback::Type::Function: {
634       bool needToFreeName = false;
635       const char* annotation = "";
636       const char* name;
637       static const size_t buflen = 1024;
638       char buf[buflen];
639 
640       if (aCallback.mName.is<Callback::NameString>()) {
641         name = aCallback.mName.as<Callback::NameString>();
642 
643       } else if (aCallback.mName.is<Callback::NameFunc>()) {
644         aCallback.mName.as<Callback::NameFunc>()(
645             mITimer, /* aAnonymize = */ false, aCallback.mClosure, buf, buflen);
646         name = buf;
647 
648       } else {
649         MOZ_ASSERT(aCallback.mName.is<Callback::NameNothing>());
650 #ifdef USE_DLADDR
651         annotation = "[from dladdr] ";
652 
653         Dl_info info;
654         void* addr = reinterpret_cast<void*>(aCallback.mCallback.c);
655         if (dladdr(addr, &info) == 0) {
656           name = "???[dladdr: failed]";
657 
658         } else if (info.dli_sname) {
659           int status;
660           name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
661           if (status == 0) {
662             // Success. Because we didn't pass in a buffer to __cxa_demangle it
663             // allocates its own one with malloc() which we must free() later.
664             MOZ_ASSERT(name);
665             needToFreeName = true;
666           } else if (status == -1) {
667             name = "???[__cxa_demangle: OOM]";
668           } else if (status == -2) {
669             name = "???[__cxa_demangle: invalid mangled name]";
670           } else if (status == -3) {
671             name = "???[__cxa_demangle: invalid argument]";
672           } else {
673             name = "???[__cxa_demangle: unexpected status value]";
674           }
675 
676         } else if (info.dli_fname) {
677           // The "#0: " prefix is necessary for `fix_stacks.py` to interpret
678           // this string as something to convert.
679           snprintf(buf, buflen, "#0: ???[%s +0x%" PRIxPTR "]\n", info.dli_fname,
680                    uintptr_t(addr) - uintptr_t(info.dli_fbase));
681           name = buf;
682 
683         } else {
684           name = "???[dladdr: no symbol or shared object obtained]";
685         }
686 #else
687         name = "???[dladdr is unimplemented or doesn't work well on this OS]";
688 #endif
689       }
690 
691       MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
692               ("[%d]    fn timer (%s %5d ms): %s%s\n", getpid(), typeStr,
693                aDelay, annotation, name));
694 
695       if (needToFreeName) {
696         free(const_cast<char*>(name));
697       }
698 
699       break;
700     }
701 
702     case Callback::Type::Interface: {
703       MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
704               ("[%d] iface timer (%s %5d ms): %p\n", getpid(), typeStr, aDelay,
705                aCallback.mCallback.i));
706       break;
707     }
708 
709     case Callback::Type::Observer: {
710       MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
711               ("[%d]   obs timer (%s %5d ms): %p\n", getpid(), typeStr, aDelay,
712                aCallback.mCallback.o));
713       break;
714     }
715 
716     case Callback::Type::Unknown:
717     default: {
718       MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
719               ("[%d]   ??? timer (%s, %5d ms)\n", getpid(), typeStr, aDelay));
720       break;
721     }
722   }
723 }
724 
GetName(nsACString & aName)725 void nsTimerImpl::GetName(nsACString& aName) {
726   MutexAutoLock lock(mMutex);
727   Callback& cb(GetCallback());
728   switch (cb.mType) {
729     case Callback::Type::Function:
730       if (cb.mName.is<Callback::NameString>()) {
731         aName.Assign(cb.mName.as<Callback::NameString>());
732       } else if (cb.mName.is<Callback::NameFunc>()) {
733         static const size_t buflen = 1024;
734         char buf[buflen];
735         cb.mName.as<Callback::NameFunc>()(mITimer, /* aAnonymize = */ true,
736                                           cb.mClosure, buf, buflen);
737         aName.Assign(buf);
738       } else {
739         MOZ_ASSERT(cb.mName.is<Callback::NameNothing>());
740         aName.AssignLiteral("Anonymous_callback_timer");
741       }
742       break;
743 
744     case Callback::Type::Interface:
745       if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.i)) {
746         named->GetName(aName);
747       } else {
748         aName.AssignLiteral("Anonymous_interface_timer");
749       }
750       break;
751 
752     case Callback::Type::Observer:
753       if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.o)) {
754         named->GetName(aName);
755       } else {
756         aName.AssignLiteral("Anonymous_observer_timer");
757       }
758       break;
759 
760     case Callback::Type::Unknown:
761       aName.AssignLiteral("Canceled_timer");
762       break;
763   }
764 }
765 
SetHolder(nsTimerImplHolder * aHolder)766 void nsTimerImpl::SetHolder(nsTimerImplHolder* aHolder) { mHolder = aHolder; }
767 
768 nsTimer::~nsTimer() = default;
769 
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const770 size_t nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
771   return aMallocSizeOf(this);
772 }
773 
774 /* static */
WithEventTarget(nsIEventTarget * aTarget)775 RefPtr<nsTimer> nsTimer::WithEventTarget(nsIEventTarget* aTarget) {
776   if (!aTarget) {
777     aTarget = mozilla::GetCurrentThreadEventTarget();
778   }
779   return do_AddRef(new nsTimer(aTarget));
780 }
781 
782 /* static */
XPCOMConstructor(nsISupports * aOuter,REFNSIID aIID,void ** aResult)783 nsresult nsTimer::XPCOMConstructor(nsISupports* aOuter, REFNSIID aIID,
784                                    void** aResult) {
785   *aResult = nullptr;
786   if (aOuter != nullptr) {
787     return NS_ERROR_NO_AGGREGATION;
788   }
789 
790   auto timer = WithEventTarget(nullptr);
791 
792   return timer->QueryInterface(aIID, aResult);
793 }
794 
795 /* static */
796 const nsTimerImpl::Callback::NameNothing nsTimerImpl::Callback::Nothing = 0;
797 
798 #ifdef MOZ_TASK_TRACER
GetTLSTraceInfo()799 void nsTimerImpl::GetTLSTraceInfo() { mTracedTask.GetTLSTraceInfo(); }
800 
GetTracedTask()801 TracedTaskCommon nsTimerImpl::GetTracedTask() { return mTracedTask; }
802 
803 #endif
804