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