Coverage Report

Created: 2018-09-25 14:53

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