Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/TimerThread.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
10
#include "nsThreadUtils.h"
11
#include "pratom.h"
12
13
#include "nsIObserverService.h"
14
#include "nsIServiceManager.h"
15
#include "mozilla/Services.h"
16
#include "mozilla/ChaosMode.h"
17
#include "mozilla/ArenaAllocator.h"
18
#include "mozilla/ArrayUtils.h"
19
#include "mozilla/BinarySearch.h"
20
21
#include <math.h>
22
23
using namespace mozilla;
24
#ifdef MOZ_TASK_TRACER
25
#include "GeckoTaskTracerImpl.h"
26
using namespace mozilla::tasktracer;
27
#endif
28
29
NS_IMPL_ISUPPORTS(TimerThread, nsIRunnable, nsIObserver)
30
31
TimerThread::TimerThread() :
32
  mInitialized(false),
33
  mMonitor("TimerThread.mMonitor"),
34
  mShutdown(false),
35
  mWaiting(false),
36
  mNotified(false),
37
  mSleeping(false),
38
  mAllowedEarlyFiringMicroseconds(0)
39
3
{
40
3
}
41
42
TimerThread::~TimerThread()
43
0
{
44
0
  mThread = nullptr;
45
0
46
0
  NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
47
0
}
48
49
nsresult
50
TimerThread::InitLocks()
51
3
{
52
3
  return NS_OK;
53
3
}
54
55
namespace {
56
57
class TimerObserverRunnable : public Runnable
58
{
59
public:
60
  explicit TimerObserverRunnable(nsIObserver* aObserver)
61
    : mozilla::Runnable("TimerObserverRunnable")
62
    , mObserver(aObserver)
63
1
  {
64
1
  }
65
66
  NS_DECL_NSIRUNNABLE
67
68
private:
69
  nsCOMPtr<nsIObserver> mObserver;
70
};
71
72
NS_IMETHODIMP
73
TimerObserverRunnable::Run()
74
1
{
75
1
  nsCOMPtr<nsIObserverService> observerService =
76
1
    mozilla::services::GetObserverService();
77
1
  if (observerService) {
78
1
    observerService->AddObserver(mObserver, "sleep_notification", false);
79
1
    observerService->AddObserver(mObserver, "wake_notification", false);
80
1
    observerService->AddObserver(mObserver, "suspend_process_notification", false);
81
1
    observerService->AddObserver(mObserver, "resume_process_notification", false);
82
1
  }
83
1
  return NS_OK;
84
1
}
85
86
} // namespace
87
88
namespace {
89
90
// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
91
// It's needed to avoid contention over the default allocator lock when
92
// firing timer events (see bug 733277).  The thread-safety is required because
93
// nsTimerEvent objects are allocated on the timer thread, and freed on another
94
// thread.  Because TimerEventAllocator has its own lock, contention over that
95
// lock is limited to the allocation and deallocation of nsTimerEvent objects.
96
//
97
// Because this is layered over ArenaAllocator, it never shrinks -- even
98
// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
99
// for later recycling.  So the amount of memory consumed will always be equal
100
// to the high-water mark consumption.  But nsTimerEvents are small and it's
101
// unusual to have more than a few hundred of them, so this shouldn't be a
102
// problem in practice.
103
104
class TimerEventAllocator
105
{
106
private:
107
  struct FreeEntry
108
  {
109
    FreeEntry* mNext;
110
  };
111
112
  ArenaAllocator<4096> mPool;
113
  FreeEntry* mFirstFree;
114
  mozilla::Monitor mMonitor;
115
116
public:
117
  TimerEventAllocator()
118
    : mPool()
119
    , mFirstFree(nullptr)
120
      // Timer thread state may be accessed during GC, so uses of this monitor
121
      // are not preserved when recording/replaying.
122
    , mMonitor("TimerEventAllocator", recordreplay::Behavior::DontPreserve)
123
1
  {
124
1
  }
125
126
  ~TimerEventAllocator()
127
0
  {
128
0
  }
129
130
  void* Alloc(size_t aSize);
131
  void Free(void* aPtr);
132
};
133
134
} // namespace
135
136
// This is a nsICancelableRunnable because we can dispatch it to Workers and
137
// those can be shut down at any time, and in these cases, Cancel() is called
138
// instead of Run().
139
class nsTimerEvent final : public CancelableRunnable
140
{
141
public:
142
  NS_IMETHOD Run() override;
143
144
  nsresult Cancel() override
145
0
  {
146
0
    mTimer->Cancel();
147
0
    return NS_OK;
148
0
  }
149
150
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
151
  NS_IMETHOD GetName(nsACString& aName) override;
152
#endif
153
154
  nsTimerEvent()
155
    : mozilla::CancelableRunnable("nsTimerEvent")
156
    , mTimer()
157
    , mGeneration(0)
158
86
  {
159
86
    // Note: We override operator new for this class, and the override is
160
86
    // fallible!
161
86
    sAllocatorUsers++;
162
86
  }
163
164
  TimeStamp mInitTime;
165
166
  static void Init();
167
  static void Shutdown();
168
  static void DeleteAllocatorIfNeeded();
169
170
  static void* operator new(size_t aSize) CPP_THROW_NEW
171
86
  {
172
86
    return sAllocator->Alloc(aSize);
173
86
  }
174
  void operator delete(void* aPtr)
175
0
  {
176
0
    sAllocator->Free(aPtr);
177
0
    DeleteAllocatorIfNeeded();
178
0
  }
179
180
  already_AddRefed<nsTimerImpl> ForgetTimer()
181
0
  {
182
0
    return mTimer.forget();
183
0
  }
184
185
  void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
186
86
  {
187
86
    mTimer = aTimer;
188
86
    mGeneration = mTimer->GetGeneration();
189
86
  }
190
191
private:
192
  nsTimerEvent(const nsTimerEvent&) = delete;
193
  nsTimerEvent& operator=(const nsTimerEvent&) = delete;
194
  nsTimerEvent& operator=(const nsTimerEvent&&) = delete;
195
196
  ~nsTimerEvent()
197
0
  {
198
0
    MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
199
0
               "This will result in us attempting to deallocate the nsTimerEvent allocator twice");
200
0
    sAllocatorUsers--;
201
0
  }
202
203
  RefPtr<nsTimerImpl> mTimer;
204
  int32_t      mGeneration;
205
206
  static TimerEventAllocator* sAllocator;
207
208
  // Timer thread state may be accessed during GC, so uses of this atomic are
209
  // not preserved when recording/replaying.
210
  static Atomic<int32_t, SequentiallyConsistent,
211
                recordreplay::Behavior::DontPreserve> sAllocatorUsers;
212
  static bool sCanDeleteAllocator;
213
};
214
215
TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
216
Atomic<int32_t, SequentiallyConsistent,
217
       recordreplay::Behavior::DontPreserve> nsTimerEvent::sAllocatorUsers;
218
bool nsTimerEvent::sCanDeleteAllocator = false;
219
220
namespace {
221
222
void*
223
TimerEventAllocator::Alloc(size_t aSize)
224
86
{
225
86
  MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
226
86
227
86
  mozilla::MonitorAutoLock lock(mMonitor);
228
86
229
86
  void* p;
230
86
  if (mFirstFree) {
231
0
    p = mFirstFree;
232
0
    mFirstFree = mFirstFree->mNext;
233
86
  } else {
234
86
    p = mPool.Allocate(aSize, fallible);
235
86
  }
236
86
237
86
  return p;
238
86
}
239
240
void
241
TimerEventAllocator::Free(void* aPtr)
242
0
{
243
0
  mozilla::MonitorAutoLock lock(mMonitor);
244
0
245
0
  FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
246
0
247
0
  entry->mNext = mFirstFree;
248
0
  mFirstFree = entry;
249
0
}
250
251
} // namespace
252
253
void
254
nsTimerEvent::Init()
255
1
{
256
1
  sAllocator = new TimerEventAllocator();
257
1
}
258
259
void
260
nsTimerEvent::Shutdown()
261
0
{
262
0
  sCanDeleteAllocator = true;
263
0
  DeleteAllocatorIfNeeded();
264
0
}
265
266
void
267
nsTimerEvent::DeleteAllocatorIfNeeded()
268
0
{
269
0
  if (sCanDeleteAllocator && sAllocatorUsers == 0) {
270
0
    delete sAllocator;
271
0
    sAllocator = nullptr;
272
0
  }
273
0
}
274
275
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
276
NS_IMETHODIMP
277
nsTimerEvent::GetName(nsACString& aName)
278
0
{
279
0
  bool current;
280
0
  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mTimer->mEventTarget->IsOnCurrentThread(&current)) && current);
281
0
282
0
  mTimer->GetName(aName);
283
0
  return NS_OK;
284
0
}
285
#endif
286
287
NS_IMETHODIMP
288
nsTimerEvent::Run()
289
0
{
290
0
  if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
291
0
    TimeStamp now = TimeStamp::Now();
292
0
    MOZ_LOG(GetTimerLog(), LogLevel::Debug,
293
0
           ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
294
0
            this, (now - mInitTime).ToMilliseconds()));
295
0
  }
296
0
297
0
  mTimer->Fire(mGeneration);
298
0
299
0
  return NS_OK;
300
0
}
301
302
nsresult
303
TimerThread::Init()
304
90
{
305
90
  mMonitor.AssertCurrentThreadOwns();
306
90
  MOZ_LOG(GetTimerLog(), LogLevel::Debug,
307
90
         ("TimerThread::Init [%d]\n", mInitialized));
308
90
309
90
  if (!mInitialized) {
310
1
    nsTimerEvent::Init();
311
1
312
1
    // We hold on to mThread to keep the thread alive.
313
1
    nsresult rv =
314
1
      NS_NewNamedThread("Timer Thread", getter_AddRefs(mThread), this);
315
1
    if (NS_FAILED(rv)) {
316
0
      mThread = nullptr;
317
1
    } else {
318
1
      RefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
319
1
      if (NS_IsMainThread()) {
320
1
        r->Run();
321
1
      } else {
322
0
        NS_DispatchToMainThread(r);
323
0
      }
324
1
    }
325
1
326
1
    mInitialized = true;
327
1
  }
328
90
329
90
  if (!mThread) {
330
0
    return NS_ERROR_FAILURE;
331
0
  }
332
90
333
90
  return NS_OK;
334
90
}
335
336
nsresult
337
TimerThread::Shutdown()
338
0
{
339
0
  MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown begin\n"));
340
0
341
0
  if (!mThread) {
342
0
    return NS_ERROR_NOT_INITIALIZED;
343
0
  }
344
0
345
0
  nsTArray<RefPtr<nsTimerImpl>> timers;
346
0
  {
347
0
    // lock scope
348
0
    MonitorAutoLock lock(mMonitor);
349
0
350
0
    mShutdown = true;
351
0
352
0
    // notify the cond var so that Run() can return
353
0
    if (mWaiting) {
354
0
      mNotified = true;
355
0
      mMonitor.Notify();
356
0
    }
357
0
358
0
    // Need to copy content of mTimers array to a local array
359
0
    // because call to timers' Cancel() (and release its self)
360
0
    // must not be done under the lock. Destructor of a callback
361
0
    // might potentially call some code reentering the same lock
362
0
    // that leads to unexpected behavior or deadlock.
363
0
    // See bug 422472.
364
0
    for (const UniquePtr<Entry>& entry : mTimers) {
365
0
      timers.AppendElement(entry->Take());
366
0
    }
367
0
368
0
    mTimers.Clear();
369
0
  }
370
0
371
0
  for (const RefPtr<nsTimerImpl>& timer : timers) {
372
0
    if (timer) {
373
0
      timer->Cancel();
374
0
    }
375
0
  }
376
0
377
0
  mThread->Shutdown();    // wait for the thread to die
378
0
379
0
  nsTimerEvent::Shutdown();
380
0
381
0
  MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown end\n"));
382
0
  return NS_OK;
383
0
}
384
385
namespace {
386
387
struct MicrosecondsToInterval
388
{
389
9
  PRIntervalTime operator[](size_t aMs) const {
390
9
    return PR_MicrosecondsToInterval(aMs);
391
9
  }
392
};
393
394
struct IntervalComparator
395
{
396
9
  int operator()(PRIntervalTime aInterval) const {
397
9
    return (0 < aInterval) ? -1 : 1;
398
9
  }
399
};
400
401
} // namespace
402
403
NS_IMETHODIMP
404
TimerThread::Run()
405
1
{
406
1
  NS_SetCurrentThreadName("Timer");
407
1
408
1
  MonitorAutoLock lock(mMonitor);
409
1
410
1
  // We need to know how many microseconds give a positive PRIntervalTime. This
411
1
  // is platform-dependent and we calculate it at runtime, finding a value |v|
412
1
  // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
413
1
  // the range [0, v) to find the ms-to-interval scale.
414
1
  uint32_t usForPosInterval = 1;
415
10
  while (PR_MicrosecondsToInterval(usForPosInterval) == 0) {
416
9
    usForPosInterval <<= 1;
417
9
  }
418
1
419
1
  size_t usIntervalResolution;
420
1
  BinarySearchIf(MicrosecondsToInterval(), 0, usForPosInterval, IntervalComparator(), &usIntervalResolution);
421
1
  MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution - 1) == 0);
422
1
  MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution) == 1);
423
1
424
1
  // Half of the amount of microseconds needed to get positive PRIntervalTime.
425
1
  // We use this to decide how to round our wait times later
426
1
  mAllowedEarlyFiringMicroseconds = usIntervalResolution / 2;
427
1
  bool forceRunNextTimer = false;
428
1
429
175
  while (!mShutdown) {
430
174
    // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
431
174
    TimeDuration waitFor;
432
174
    bool forceRunThisTimer = forceRunNextTimer;
433
174
    forceRunNextTimer = false;
434
174
435
174
    if (mSleeping) {
436
0
      // Sleep for 0.1 seconds while not firing timers.
437
0
      uint32_t milliseconds = 100;
438
0
      if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
439
0
        milliseconds = ChaosMode::randomUint32LessThan(200);
440
0
      }
441
0
      waitFor = TimeDuration::FromMilliseconds(milliseconds);
442
174
    } else {
443
174
      waitFor = TimeDuration::Forever();
444
174
      TimeStamp now = TimeStamp::Now();
445
174
446
174
      RemoveLeadingCanceledTimersInternal();
447
174
448
174
      if (!mTimers.IsEmpty()) {
449
174
        if (now >= mTimers[0]->Value()->mTimeout || forceRunThisTimer) {
450
86
    next:
451
86
          // NB: AddRef before the Release under RemoveTimerInternal to avoid
452
86
          // mRefCnt passing through zero, in case all other refs than the one
453
86
          // from mTimers have gone away (the last non-mTimers[i]-ref's Release
454
86
          // must be racing with us, blocked in gThread->RemoveTimer waiting
455
86
          // for TimerThread::mMonitor, under nsTimerImpl::Release.
456
86
457
86
          RefPtr<nsTimerImpl> timerRef(mTimers[0]->Take());
458
86
          RemoveFirstTimerInternal();
459
86
460
86
          MOZ_LOG(GetTimerLog(), LogLevel::Debug,
461
86
                 ("Timer thread woke up %fms from when it was supposed to\n",
462
86
                  fabs((now - timerRef->mTimeout).ToMilliseconds())));
463
86
464
86
          // We are going to let the call to PostTimerEvent here handle the
465
86
          // release of the timer so that we don't end up releasing the timer
466
86
          // on the TimerThread instead of on the thread it targets.
467
86
          timerRef = PostTimerEvent(timerRef.forget());
468
86
469
86
          if (timerRef) {
470
0
            // We got our reference back due to an error.
471
0
            // Unhook the nsRefPtr, and release manually so we can get the
472
0
            // refcount.
473
0
            nsrefcnt rc = timerRef.forget().take()->Release();
474
0
            (void)rc;
475
0
476
0
            // The nsITimer interface requires that its users keep a reference
477
0
            // to the timers they use while those timers are initialized but
478
0
            // have not yet fired.  If this ever happens, it is a bug in the
479
0
            // code that created and used the timer.
480
0
            //
481
0
            // Further, note that this should never happen even with a
482
0
            // misbehaving user, because nsTimerImpl::Release checks for a
483
0
            // refcount of 1 with an armed timer (a timer whose only reference
484
0
            // is from the timer thread) and when it hits this will remove the
485
0
            // timer from the timer thread and thus destroy the last reference,
486
0
            // preventing this situation from occurring.
487
0
            MOZ_ASSERT(rc != 0, "destroyed timer off its target thread!");
488
0
          }
489
86
490
86
          if (mShutdown) {
491
0
            break;
492
0
          }
493
86
494
86
          // Update now, as PostTimerEvent plus the locking may have taken a
495
86
          // tick or two, and we may goto next below.
496
86
          now = TimeStamp::Now();
497
86
        }
498
174
      }
499
174
500
174
      RemoveLeadingCanceledTimersInternal();
501
174
502
174
      if (!mTimers.IsEmpty()) {
503
95
        TimeStamp timeout = mTimers[0]->Value()->mTimeout;
504
95
505
95
        // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
506
95
        // is due now or overdue.
507
95
        //
508
95
        // Note that we can only sleep for integer values of a certain
509
95
        // resolution. We use mAllowedEarlyFiringMicroseconds, calculated
510
95
        // before, to do the optimal rounding (i.e., of how to decide what
511
95
        // interval is so small we should not wait at all).
512
95
        double microseconds = (timeout - now).ToMilliseconds() * 1000;
513
95
514
95
        if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
515
0
          // The mean value of sFractions must be 1 to ensure that
516
0
          // the average of a long sequence of timeouts converges to the
517
0
          // actual sum of their times.
518
0
          static const float sFractions[] = {
519
0
            0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
520
0
          };
521
0
          microseconds *=
522
0
            sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
523
0
          forceRunNextTimer = true;
524
0
        }
525
95
526
95
        if (microseconds < mAllowedEarlyFiringMicroseconds) {
527
0
          forceRunNextTimer = false;
528
0
          goto next; // round down; execute event now
529
0
        }
530
95
        waitFor = TimeDuration::FromMicroseconds(microseconds);
531
95
        if (waitFor.IsZero()) {
532
0
          // round up, wait the minimum time we can wait
533
0
          waitFor = TimeDuration::FromMicroseconds(1);
534
0
        }
535
95
      }
536
174
537
174
      if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
538
0
        if (waitFor == TimeDuration::Forever())
539
0
          MOZ_LOG(GetTimerLog(), LogLevel::Debug,
540
0
                 ("waiting forever\n"));
541
0
        else
542
0
          MOZ_LOG(GetTimerLog(), LogLevel::Debug,
543
0
                 ("waiting for %f\n", waitFor.ToMilliseconds()));
544
0
      }
545
174
    }
546
174
547
174
    mWaiting = true;
548
174
    mNotified = false;
549
174
    mMonitor.Wait(waitFor);
550
174
    if (mNotified) {
551
87
      forceRunNextTimer = false;
552
87
    }
553
174
    mWaiting = false;
554
174
  }
555
1
556
1
  return NS_OK;
557
1
}
558
559
nsresult
560
TimerThread::AddTimer(nsTimerImpl* aTimer)
561
90
{
562
90
  MonitorAutoLock lock(mMonitor);
563
90
564
90
  if (!aTimer->mEventTarget) {
565
0
    return NS_ERROR_NOT_INITIALIZED;
566
0
  }
567
90
568
90
  nsresult rv = Init();
569
90
  if (NS_FAILED(rv)) {
570
0
    return rv;
571
0
  }
572
90
573
90
  // Add the timer to our list.
574
90
  if(!AddTimerInternal(aTimer)) {
575
0
    return NS_ERROR_OUT_OF_MEMORY;
576
0
  }
577
90
578
90
  // Awaken the timer thread.
579
90
  if (mWaiting && mTimers[0]->Value() == aTimer) {
580
83
    mNotified = true;
581
83
    mMonitor.Notify();
582
83
  }
583
90
584
90
  return NS_OK;
585
90
}
586
587
nsresult
588
TimerThread::RemoveTimer(nsTimerImpl* aTimer)
589
258
{
590
258
  MonitorAutoLock lock(mMonitor);
591
258
592
258
  // Remove the timer from our array.  Tell callers that aTimer was not found
593
258
  // by returning NS_ERROR_NOT_AVAILABLE.
594
258
595
258
  if (!RemoveTimerInternal(aTimer)) {
596
254
    return NS_ERROR_NOT_AVAILABLE;
597
254
  }
598
4
599
4
  // Awaken the timer thread.
600
4
  if (mWaiting) {
601
4
    mNotified = true;
602
4
    mMonitor.Notify();
603
4
  }
604
4
605
4
  return NS_OK;
606
4
}
607
608
TimeStamp
609
TimerThread::FindNextFireTimeForCurrentThread(TimeStamp aDefault, uint32_t aSearchBound)
610
0
{
611
0
  MonitorAutoLock lock(mMonitor);
612
0
  TimeStamp timeStamp = aDefault;
613
0
  uint32_t index = 0;
614
0
615
#ifdef DEBUG
616
  TimeStamp firstTimeStamp;
617
  Entry* initialFirstEntry = nullptr;
618
  if (!mTimers.IsEmpty()) {
619
    initialFirstEntry = mTimers[0].get();
620
    firstTimeStamp = mTimers[0]->Timeout();
621
  }
622
#endif
623
624
0
  auto end = mTimers.end();
625
0
  while(end != mTimers.begin()) {
626
0
    nsTimerImpl* timer = mTimers[0]->Value();
627
0
    if (timer) {
628
0
      if (timer->mTimeout > aDefault) {
629
0
        timeStamp = aDefault;
630
0
        break;
631
0
      }
632
0
633
0
      // Don't yield to timers created with the *_LOW_PRIORITY type.
634
0
      if (!timer->IsLowPriority()) {
635
0
        bool isOnCurrentThread = false;
636
0
        nsresult rv = timer->mEventTarget->IsOnCurrentThread(&isOnCurrentThread);
637
0
        if (NS_SUCCEEDED(rv) && isOnCurrentThread) {
638
0
          timeStamp = timer->mTimeout;
639
0
          break;
640
0
        }
641
0
      }
642
0
643
0
      if (++index > aSearchBound) {
644
0
        // Track the currently highest timeout so that we can bail out when we
645
0
        // reach the bound or when we find a timer for the current thread.
646
0
        // This won't give accurate information if we stop before finding
647
0
        // any timer for the current thread, but at least won't report too
648
0
        // long idle period.
649
0
        timeStamp = timer->mTimeout;
650
0
        break;
651
0
      }
652
0
    }
653
0
654
0
    std::pop_heap(mTimers.begin(), end, Entry::UniquePtrLessThan);
655
0
    --end;
656
0
  }
657
0
658
0
  while (end != mTimers.end()) {
659
0
    ++end;
660
0
    std::push_heap(mTimers.begin(), end, Entry::UniquePtrLessThan);
661
0
  }
662
0
663
#ifdef DEBUG
664
  if (!mTimers.IsEmpty()) {
665
    if (firstTimeStamp != mTimers[0]->Timeout()) {
666
      TimeStamp now = TimeStamp::Now();
667
      printf_stderr("firstTimeStamp %f, mTimers[0]->Timeout() %f, "
668
                    "initialFirstTimer %p, current first %p\n",
669
                    (firstTimeStamp - now).ToMilliseconds(),
670
                    (mTimers[0]->Timeout() - now).ToMilliseconds(),
671
                    initialFirstEntry, mTimers[0].get());
672
    }
673
  }
674
  MOZ_ASSERT_IF(!mTimers.IsEmpty(), firstTimeStamp == mTimers[0]->Timeout());
675
#endif
676
677
0
  return timeStamp;
678
0
}
679
680
// This function must be called from within a lock
681
bool
682
TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
683
90
{
684
90
  mMonitor.AssertCurrentThreadOwns();
685
90
  if (mShutdown) {
686
0
    return false;
687
0
  }
688
90
689
90
  TimeStamp now = TimeStamp::Now();
690
90
691
90
  UniquePtr<Entry>* entry = mTimers.AppendElement(
692
90
    MakeUnique<Entry>(now, aTimer->mTimeout, aTimer), mozilla::fallible);
693
90
  if (!entry) {
694
0
    return false;
695
0
  }
696
90
697
90
  std::push_heap(mTimers.begin(), mTimers.end(), Entry::UniquePtrLessThan);
698
90
699
#ifdef MOZ_TASK_TRACER
700
  // Caller of AddTimer is the parent task of its timer event, so we store the
701
  // TraceInfo here for later used.
702
  aTimer->GetTLSTraceInfo();
703
#endif
704
705
90
  return true;
706
90
}
707
708
bool
709
TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
710
258
{
711
258
  mMonitor.AssertCurrentThreadOwns();
712
258
  if (!aTimer || !aTimer->mHolder) {
713
254
    return false;
714
254
  }
715
4
  aTimer->mHolder->Forget(aTimer);
716
4
  return true;
717
4
}
718
719
void
720
TimerThread::RemoveLeadingCanceledTimersInternal()
721
348
{
722
348
  mMonitor.AssertCurrentThreadOwns();
723
348
724
348
  // Move all canceled timers from the front of the list to
725
348
  // the back of the list using std::pop_heap().  We do this
726
348
  // without actually removing them from the list so we can
727
348
  // modify the nsTArray in a single bulk operation.
728
348
  auto sortedEnd = mTimers.end();
729
352
  while (sortedEnd != mTimers.begin() && !mTimers[0]->Value()) {
730
4
    std::pop_heap(mTimers.begin(), sortedEnd, Entry::UniquePtrLessThan);
731
4
    --sortedEnd;
732
4
  }
733
348
734
348
  // If there were no canceled timers then we are done.
735
348
  if (sortedEnd == mTimers.end()) {
736
344
    return;
737
344
  }
738
4
739
4
  // Finally, remove the canceled timers from the back of the
740
4
  // nsTArray.  Note, since std::pop_heap() uses iterators
741
4
  // we must convert to nsTArray indices and number of
742
4
  // elements here.
743
4
  mTimers.RemoveElementsAt(sortedEnd - mTimers.begin(),
744
4
                           mTimers.end() - sortedEnd);
745
4
}
746
747
void
748
TimerThread::RemoveFirstTimerInternal()
749
86
{
750
86
  mMonitor.AssertCurrentThreadOwns();
751
86
  MOZ_ASSERT(!mTimers.IsEmpty());
752
86
  std::pop_heap(mTimers.begin(), mTimers.end(), Entry::UniquePtrLessThan);
753
86
  mTimers.RemoveLastElement();
754
86
}
755
756
already_AddRefed<nsTimerImpl>
757
TimerThread::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
758
86
{
759
86
  mMonitor.AssertCurrentThreadOwns();
760
86
761
86
  RefPtr<nsTimerImpl> timer(aTimerRef);
762
86
  if (!timer->mEventTarget) {
763
0
    NS_ERROR("Attempt to post timer event to NULL event target");
764
0
    return timer.forget();
765
0
  }
766
86
767
86
  // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
768
86
769
86
  // Since we already addref'd 'timer', we don't need to addref here.
770
86
  // We will release either in ~nsTimerEvent(), or pass the reference back to
771
86
  // the caller. We need to copy the generation number from this timer into the
772
86
  // event, so we can avoid firing a timer that was re-initialized after being
773
86
  // canceled.
774
86
775
86
  RefPtr<nsTimerEvent> event = new nsTimerEvent;
776
86
  if (!event) {
777
0
    return timer.forget();
778
0
  }
779
86
780
86
  if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
781
0
    event->mInitTime = TimeStamp::Now();
782
0
  }
783
86
784
#ifdef MOZ_TASK_TRACER
785
  // During the dispatch of TimerEvent, we overwrite the current TraceInfo
786
  // partially with the info saved in timer earlier, and restore it back by
787
  // AutoSaveCurTraceInfo.
788
  AutoSaveCurTraceInfo saveCurTraceInfo;
789
  (timer->GetTracedTask()).SetTLSTraceInfo();
790
#endif
791
792
86
  nsCOMPtr<nsIEventTarget> target = timer->mEventTarget;
793
86
  event->SetTimer(timer.forget());
794
86
795
86
  nsresult rv;
796
86
  {
797
86
    // We release mMonitor around the Dispatch because if this timer is targeted
798
86
    // at the TimerThread we'll deadlock.
799
86
    MonitorAutoUnlock unlock(mMonitor);
800
86
    rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
801
86
  }
802
86
803
86
  if (NS_FAILED(rv)) {
804
0
    timer = event->ForgetTimer();
805
0
    RemoveTimerInternal(timer);
806
0
    return timer.forget();
807
0
  }
808
86
809
86
  return nullptr;
810
86
}
811
812
void
813
TimerThread::DoBeforeSleep()
814
0
{
815
0
  // Mainthread
816
0
  MonitorAutoLock lock(mMonitor);
817
0
  mSleeping = true;
818
0
}
819
820
// Note: wake may be notified without preceding sleep notification
821
void
822
TimerThread::DoAfterSleep()
823
0
{
824
0
  // Mainthread
825
0
  MonitorAutoLock lock(mMonitor);
826
0
  mSleeping = false;
827
0
828
0
  // Wake up the timer thread to re-process the array to ensure the sleep delay is correct,
829
0
  // and fire any expired timers (perhaps quite a few)
830
0
  mNotified = true;
831
0
  mMonitor.Notify();
832
0
}
833
834
835
NS_IMETHODIMP
836
TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
837
                     const char16_t* /* aData */)
838
0
{
839
0
  if (strcmp(aTopic, "sleep_notification") == 0 ||
840
0
      strcmp(aTopic, "suspend_process_notification") == 0) {
841
0
    DoBeforeSleep();
842
0
  } else if (strcmp(aTopic, "wake_notification") == 0 ||
843
0
             strcmp(aTopic, "resume_process_notification") == 0) {
844
0
    DoAfterSleep();
845
0
  }
846
0
847
0
  return NS_OK;
848
0
}
849
850
uint32_t
851
TimerThread::AllowedEarlyFiringMicroseconds() const
852
0
{
853
0
  return mAllowedEarlyFiringMicroseconds;
854
0
}