1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsTimerImpl.h"
8 #include "TimerThread.h"
10 #include "GeckoProfiler.h"
11 #include "nsThreadUtils.h"
14 #include "nsIObserverService.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 #include "mozilla/OperatorNewExtensions.h"
21 #include "mozilla/StaticPrefs_timer.h"
25 using namespace mozilla
;
27 NS_IMPL_ISUPPORTS_INHERITED(TimerThread
, Runnable
, nsIObserver
)
29 TimerThread::TimerThread()
30 : Runnable("TimerThread"),
32 mMonitor("TimerThread.mMonitor"),
37 mAllowedEarlyFiringMicroseconds(0) {}
39 TimerThread::~TimerThread() {
42 NS_ASSERTION(mTimers
.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
45 nsresult
TimerThread::InitLocks() { return NS_OK
; }
49 class TimerObserverRunnable
: public Runnable
{
51 explicit TimerObserverRunnable(nsIObserver
* aObserver
)
52 : mozilla::Runnable("TimerObserverRunnable"), mObserver(aObserver
) {}
57 nsCOMPtr
<nsIObserver
> mObserver
;
61 TimerObserverRunnable::Run() {
62 nsCOMPtr
<nsIObserverService
> observerService
=
63 mozilla::services::GetObserverService();
64 if (observerService
) {
65 observerService
->AddObserver(mObserver
, "sleep_notification", false);
66 observerService
->AddObserver(mObserver
, "wake_notification", false);
67 observerService
->AddObserver(mObserver
, "suspend_process_notification",
69 observerService
->AddObserver(mObserver
, "resume_process_notification",
79 // TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
80 // It's needed to avoid contention over the default allocator lock when
81 // firing timer events (see bug 733277). The thread-safety is required because
82 // nsTimerEvent objects are allocated on the timer thread, and freed on another
83 // thread. Because TimerEventAllocator has its own lock, contention over that
84 // lock is limited to the allocation and deallocation of nsTimerEvent objects.
86 // Because this is layered over ArenaAllocator, it never shrinks -- even
87 // "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
88 // for later recycling. So the amount of memory consumed will always be equal
89 // to the high-water mark consumption. But nsTimerEvents are small and it's
90 // unusual to have more than a few hundred of them, so this shouldn't be a
91 // problem in practice.
93 class TimerEventAllocator
{
99 ArenaAllocator
<4096> mPool
;
100 FreeEntry
* mFirstFree
;
101 mozilla::Monitor mMonitor
;
104 TimerEventAllocator()
105 : mPool(), mFirstFree(nullptr), mMonitor("TimerEventAllocator") {}
107 ~TimerEventAllocator() = default;
109 void* Alloc(size_t aSize
);
110 void Free(void* aPtr
);
115 // This is a nsICancelableRunnable because we can dispatch it to Workers and
116 // those can be shut down at any time, and in these cases, Cancel() is called
118 class nsTimerEvent final
: public CancelableRunnable
{
120 NS_IMETHOD
Run() override
;
122 nsresult
Cancel() override
{
127 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
128 NS_IMETHOD
GetName(nsACString
& aName
) override
;
131 explicit nsTimerEvent(already_AddRefed
<nsTimerImpl
> aTimer
,
132 ProfilerThreadId aTimerThreadId
)
133 : mozilla::CancelableRunnable("nsTimerEvent"),
135 mGeneration(mTimer
->GetGeneration()),
136 mTimerThreadId(aTimerThreadId
) {
137 // Note: We override operator new for this class, and the override is
141 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
) ||
142 profiler_thread_is_being_profiled_for_markers(mTimerThreadId
)) {
143 mInitTime
= TimeStamp::Now();
148 static void Shutdown();
149 static void DeleteAllocatorIfNeeded();
151 static void* operator new(size_t aSize
) noexcept(true) {
152 return sAllocator
->Alloc(aSize
);
154 void operator delete(void* aPtr
) {
155 sAllocator
->Free(aPtr
);
157 DeleteAllocatorIfNeeded();
160 already_AddRefed
<nsTimerImpl
> ForgetTimer() { return mTimer
.forget(); }
163 nsTimerEvent(const nsTimerEvent
&) = delete;
164 nsTimerEvent
& operator=(const nsTimerEvent
&) = delete;
165 nsTimerEvent
& operator=(const nsTimerEvent
&&) = delete;
168 MOZ_ASSERT(!sCanDeleteAllocator
|| sAllocatorUsers
> 0,
169 "This will result in us attempting to deallocate the "
170 "nsTimerEvent allocator twice");
174 RefPtr
<nsTimerImpl
> mTimer
;
175 const int32_t mGeneration
;
176 ProfilerThreadId mTimerThreadId
;
178 static TimerEventAllocator
* sAllocator
;
180 static Atomic
<int32_t, SequentiallyConsistent
> sAllocatorUsers
;
181 static Atomic
<bool, SequentiallyConsistent
> sCanDeleteAllocator
;
184 TimerEventAllocator
* nsTimerEvent::sAllocator
= nullptr;
185 Atomic
<int32_t, SequentiallyConsistent
> nsTimerEvent::sAllocatorUsers
;
186 Atomic
<bool, SequentiallyConsistent
> nsTimerEvent::sCanDeleteAllocator
;
190 void* TimerEventAllocator::Alloc(size_t aSize
) {
191 MOZ_ASSERT(aSize
== sizeof(nsTimerEvent
));
193 mozilla::MonitorAutoLock
lock(mMonitor
);
198 mFirstFree
= mFirstFree
->mNext
;
200 p
= mPool
.Allocate(aSize
, fallible
);
206 void TimerEventAllocator::Free(void* aPtr
) {
207 mozilla::MonitorAutoLock
lock(mMonitor
);
209 FreeEntry
* entry
= reinterpret_cast<FreeEntry
*>(aPtr
);
211 entry
->mNext
= mFirstFree
;
217 void nsTimerEvent::Init() { sAllocator
= new TimerEventAllocator(); }
219 void nsTimerEvent::Shutdown() {
220 sCanDeleteAllocator
= true;
221 DeleteAllocatorIfNeeded();
224 void nsTimerEvent::DeleteAllocatorIfNeeded() {
225 if (sCanDeleteAllocator
&& sAllocatorUsers
== 0) {
227 sAllocator
= nullptr;
231 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
233 nsTimerEvent::GetName(nsACString
& aName
) {
236 NS_SUCCEEDED(mTimer
->mEventTarget
->IsOnCurrentThread(¤t
)) &&
239 mTimer
->GetName(aName
);
245 nsTimerEvent::Run() {
246 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
)) {
247 TimeStamp now
= TimeStamp::Now();
248 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
249 ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n", this,
250 (now
- mInitTime
).ToMilliseconds()));
253 if (profiler_thread_is_being_profiled_for_markers(mTimerThreadId
)) {
255 mTimer
->GetName(name
);
256 PROFILER_MARKER_TEXT(
257 "PostTimerEvent", OTHER
,
258 MarkerOptions(MOZ_LIKELY(mInitTime
)
259 ? MarkerTiming::IntervalUntilNowFrom(mInitTime
)
260 : MarkerTiming::InstantNow(),
261 MarkerThreadId(mTimerThreadId
)),
265 mTimer
->Fire(mGeneration
);
270 nsresult
TimerThread::Init() {
271 mMonitor
.AssertCurrentThreadOwns();
272 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
273 ("TimerThread::Init [%d]\n", mInitialized
));
276 nsTimerEvent::Init();
278 // We hold on to mThread to keep the thread alive.
279 nsresult rv
= NS_NewNamedThread("Timer", getter_AddRefs(mThread
), this);
283 RefPtr
<TimerObserverRunnable
> r
= new TimerObserverRunnable(this);
284 if (NS_IsMainThread()) {
287 NS_DispatchToMainThread(r
);
295 return NS_ERROR_FAILURE
;
301 nsresult
TimerThread::Shutdown() {
302 MOZ_LOG(GetTimerLog(), LogLevel::Debug
, ("TimerThread::Shutdown begin\n"));
305 return NS_ERROR_NOT_INITIALIZED
;
308 nsTArray
<RefPtr
<nsTimerImpl
>> timers
;
311 MonitorAutoLock
lock(mMonitor
);
315 // notify the cond var so that Run() can return
321 // Need to copy content of mTimers array to a local array
322 // because call to timers' Cancel() (and release its self)
323 // must not be done under the lock. Destructor of a callback
324 // might potentially call some code reentering the same lock
325 // that leads to unexpected behavior or deadlock.
327 for (const UniquePtr
<Entry
>& entry
: mTimers
) {
328 timers
.AppendElement(entry
->Take());
334 for (const RefPtr
<nsTimerImpl
>& timer
: timers
) {
340 mThread
->Shutdown(); // wait for the thread to die
342 nsTimerEvent::Shutdown();
344 MOZ_LOG(GetTimerLog(), LogLevel::Debug
, ("TimerThread::Shutdown end\n"));
350 struct MicrosecondsToInterval
{
351 PRIntervalTime
operator[](size_t aMs
) const {
352 return PR_MicrosecondsToInterval(aMs
);
356 struct IntervalComparator
{
357 int operator()(PRIntervalTime aInterval
) const {
358 return (0 < aInterval
) ? -1 : 1;
366 MonitorAutoLock
lock(mMonitor
);
368 mProfilerThreadId
= profiler_current_thread_id();
370 // We need to know how many microseconds give a positive PRIntervalTime. This
371 // is platform-dependent and we calculate it at runtime, finding a value |v|
372 // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
373 // the range [0, v) to find the ms-to-interval scale.
374 uint32_t usForPosInterval
= 1;
375 while (PR_MicrosecondsToInterval(usForPosInterval
) == 0) {
376 usForPosInterval
<<= 1;
379 size_t usIntervalResolution
;
380 BinarySearchIf(MicrosecondsToInterval(), 0, usForPosInterval
,
381 IntervalComparator(), &usIntervalResolution
);
382 MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution
- 1) == 0);
383 MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution
) == 1);
385 // Half of the amount of microseconds needed to get positive PRIntervalTime.
386 // We use this to decide how to round our wait times later
387 mAllowedEarlyFiringMicroseconds
= usIntervalResolution
/ 2;
388 bool forceRunNextTimer
= false;
391 // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
392 TimeDuration waitFor
;
393 bool forceRunThisTimer
= forceRunNextTimer
;
394 forceRunNextTimer
= false;
397 // Sleep for 0.1 seconds while not firing timers.
398 uint32_t milliseconds
= 100;
399 if (ChaosMode::isActive(ChaosFeature::TimerScheduling
)) {
400 milliseconds
= ChaosMode::randomUint32LessThan(200);
402 waitFor
= TimeDuration::FromMilliseconds(milliseconds
);
404 waitFor
= TimeDuration::Forever();
405 TimeStamp now
= TimeStamp::Now();
407 RemoveLeadingCanceledTimersInternal();
409 if (!mTimers
.IsEmpty()) {
410 if (now
>= mTimers
[0]->Value()->mTimeout
|| forceRunThisTimer
) {
412 // NB: AddRef before the Release under RemoveTimerInternal to avoid
413 // mRefCnt passing through zero, in case all other refs than the one
414 // from mTimers have gone away (the last non-mTimers[i]-ref's Release
415 // must be racing with us, blocked in gThread->RemoveTimer waiting
416 // for TimerThread::mMonitor, under nsTimerImpl::Release.
418 RefPtr
<nsTimerImpl
> timerRef(mTimers
[0]->Take());
419 RemoveFirstTimerInternal();
421 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
422 ("Timer thread woke up %fms from when it was supposed to\n",
423 fabs((now
- timerRef
->mTimeout
).ToMilliseconds())));
425 // We are going to let the call to PostTimerEvent here handle the
426 // release of the timer so that we don't end up releasing the timer
427 // on the TimerThread instead of on the thread it targets.
429 LogTimerEvent::Run
run(timerRef
.get());
430 PostTimerEvent(timerRef
.forget());
437 // Update now, as PostTimerEvent plus the locking may have taken a
438 // tick or two, and we may goto next below.
439 now
= TimeStamp::Now();
443 RemoveLeadingCanceledTimersInternal();
445 if (!mTimers
.IsEmpty()) {
446 TimeStamp timeout
= mTimers
[0]->Value()->mTimeout
;
448 // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
449 // is due now or overdue.
451 // Note that we can only sleep for integer values of a certain
452 // resolution. We use mAllowedEarlyFiringMicroseconds, calculated
453 // before, to do the optimal rounding (i.e., of how to decide what
454 // interval is so small we should not wait at all).
455 double microseconds
= (timeout
- now
).ToMilliseconds() * 1000;
457 if (ChaosMode::isActive(ChaosFeature::TimerScheduling
)) {
458 // The mean value of sFractions must be 1 to ensure that
459 // the average of a long sequence of timeouts converges to the
460 // actual sum of their times.
461 static const float sFractions
[] = {0.0f
, 0.25f
, 0.5f
, 0.75f
,
463 microseconds
*= sFractions
[ChaosMode::randomUint32LessThan(
464 ArrayLength(sFractions
))];
465 forceRunNextTimer
= true;
468 if (microseconds
< mAllowedEarlyFiringMicroseconds
) {
469 forceRunNextTimer
= false;
470 goto next
; // round down; execute event now
472 waitFor
= TimeDuration::FromMicroseconds(microseconds
);
473 if (waitFor
.IsZero()) {
474 // round up, wait the minimum time we can wait
475 waitFor
= TimeDuration::FromMicroseconds(1);
479 if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug
)) {
480 if (waitFor
== TimeDuration::Forever())
481 MOZ_LOG(GetTimerLog(), LogLevel::Debug
, ("waiting forever\n"));
483 MOZ_LOG(GetTimerLog(), LogLevel::Debug
,
484 ("waiting for %f\n", waitFor
.ToMilliseconds()));
491 AUTO_PROFILER_TRACING_MARKER("TimerThread", "Wait", OTHER
);
492 mMonitor
.Wait(waitFor
);
495 forceRunNextTimer
= false;
503 nsresult
TimerThread::AddTimer(nsTimerImpl
* aTimer
,
504 const MutexAutoLock
& aProofOfLock
) {
505 MonitorAutoLock
lock(mMonitor
);
507 if (!aTimer
->mEventTarget
) {
508 return NS_ERROR_NOT_INITIALIZED
;
511 nsresult rv
= Init();
516 // Add the timer to our list.
517 if (!AddTimerInternal(aTimer
)) {
518 return NS_ERROR_OUT_OF_MEMORY
;
521 // Awaken the timer thread.
522 if (mWaiting
&& mTimers
[0]->Value() == aTimer
) {
527 if (profiler_thread_is_being_profiled_for_markers(mProfilerThreadId
)) {
529 static constexpr Span
<const char> MarkerTypeName() {
530 return MakeStringSpan("Timer");
532 static void StreamJSONMarkerData(
533 baseprofiler::SpliceableJSONWriter
& aWriter
,
534 const ProfilerString8View
& aTimerName
, uint32_t aDelay
,
535 MarkerThreadId aThreadId
) {
536 aWriter
.StringProperty("name", aTimerName
);
537 aWriter
.IntProperty("delay", aDelay
);
538 if (!aThreadId
.IsUnspecified()) {
539 // Tech note: If `ToNumber()` returns a uint64_t, the conversion to
540 // int64_t is "implementation-defined" before C++20. This is
541 // acceptable here, because this is a one-way conversion to a unique
542 // identifier that's used to visually separate data by thread on the
544 aWriter
.IntProperty("threadId", static_cast<int64_t>(
545 aThreadId
.ThreadId().ToNumber()));
548 static MarkerSchema
MarkerTypeDisplay() {
549 using MS
= MarkerSchema
;
550 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
551 schema
.AddKeyLabelFormatSearchable("name", "Name", MS::Format::String
,
552 MS::Searchable::Searchable
);
553 schema
.AddKeyLabelFormat("delay", "Delay", MS::Format::Milliseconds
);
554 schema
.SetTableLabel(
555 "{marker.name} - {marker.data.name} - {marker.data.delay}");
561 aTimer
->GetName(name
, aProofOfLock
);
563 nsLiteralCString
prefix("Anonymous_");
565 "AddTimer", geckoprofiler::category::OTHER
,
566 MarkerOptions(MarkerThreadId(mProfilerThreadId
),
567 MarkerStack::MaybeCapture(
568 StringHead(name
, prefix
.Length()) == prefix
)),
569 TimerMarker
{}, name
, aTimer
->mDelay
.ToMilliseconds(),
570 MarkerThreadId::CurrentThread());
576 nsresult
TimerThread::RemoveTimer(nsTimerImpl
* aTimer
,
577 const MutexAutoLock
& aProofOfLock
) {
578 MonitorAutoLock
lock(mMonitor
);
580 // Remove the timer from our array. Tell callers that aTimer was not found
581 // by returning NS_ERROR_NOT_AVAILABLE.
583 if (!RemoveTimerInternal(aTimer
)) {
584 return NS_ERROR_NOT_AVAILABLE
;
587 // Awaken the timer thread.
593 if (profiler_thread_is_being_profiled_for_markers(mProfilerThreadId
)) {
595 aTimer
->GetName(name
, aProofOfLock
);
597 nsLiteralCString
prefix("Anonymous_");
598 PROFILER_MARKER_TEXT(
599 "RemoveTimer", OTHER
,
600 MarkerOptions(MarkerThreadId(mProfilerThreadId
),
601 MarkerStack::MaybeCapture(
602 StringHead(name
, prefix
.Length()) == prefix
)),
609 TimeStamp
TimerThread::FindNextFireTimeForCurrentThread(TimeStamp aDefault
,
610 uint32_t aSearchBound
) {
611 MonitorAutoLock
lock(mMonitor
);
612 TimeStamp timeStamp
= aDefault
;
616 TimeStamp firstTimeStamp
;
617 Entry
* initialFirstEntry
= nullptr;
618 if (!mTimers
.IsEmpty()) {
619 initialFirstEntry
= mTimers
[0].get();
620 firstTimeStamp
= mTimers
[0]->Timeout();
624 auto end
= mTimers
.end();
625 while (end
!= mTimers
.begin()) {
626 nsTimerImpl
* timer
= mTimers
[0]->Value();
628 if (timer
->mTimeout
> aDefault
) {
629 timeStamp
= aDefault
;
633 // Don't yield to timers created with the *_LOW_PRIORITY type.
634 if (!timer
->IsLowPriority()) {
635 bool isOnCurrentThread
= false;
637 timer
->mEventTarget
->IsOnCurrentThread(&isOnCurrentThread
);
638 if (NS_SUCCEEDED(rv
) && isOnCurrentThread
) {
639 timeStamp
= timer
->mTimeout
;
644 if (++index
> aSearchBound
) {
645 // Track the currently highest timeout so that we can bail out when we
646 // reach the bound or when we find a timer for the current thread.
647 // This won't give accurate information if we stop before finding
648 // any timer for the current thread, but at least won't report too
650 timeStamp
= timer
->mTimeout
;
655 std::pop_heap(mTimers
.begin(), end
, Entry::UniquePtrLessThan
);
659 while (end
!= mTimers
.end()) {
661 std::push_heap(mTimers
.begin(), end
, Entry::UniquePtrLessThan
);
665 if (!mTimers
.IsEmpty()) {
666 if (firstTimeStamp
!= mTimers
[0]->Timeout()) {
667 TimeStamp now
= TimeStamp::Now();
669 "firstTimeStamp %f, mTimers[0]->Timeout() %f, "
670 "initialFirstTimer %p, current first %p\n",
671 (firstTimeStamp
- now
).ToMilliseconds(),
672 (mTimers
[0]->Timeout() - now
).ToMilliseconds(), initialFirstEntry
,
676 MOZ_ASSERT_IF(!mTimers
.IsEmpty(), firstTimeStamp
== mTimers
[0]->Timeout());
682 // This function must be called from within a lock
683 // Also: we hold the mutex for the nsTimerImpl.
684 bool TimerThread::AddTimerInternal(nsTimerImpl
* aTimer
) {
685 mMonitor
.AssertCurrentThreadOwns();
686 aTimer
->mMutex
.AssertCurrentThreadOwns();
691 TimeStamp now
= TimeStamp::Now();
693 LogTimerEvent::LogDispatch(aTimer
);
695 UniquePtr
<Entry
>* entry
= mTimers
.AppendElement(
696 MakeUnique
<Entry
>(now
, aTimer
->mTimeout
, aTimer
), mozilla::fallible
);
701 std::push_heap(mTimers
.begin(), mTimers
.end(), Entry::UniquePtrLessThan
);
706 // This function must be called from within a lock
707 // Also: we hold the mutex for the nsTimerImpl.
708 bool TimerThread::RemoveTimerInternal(nsTimerImpl
* aTimer
) {
709 mMonitor
.AssertCurrentThreadOwns();
710 aTimer
->mMutex
.AssertCurrentThreadOwns();
711 if (!aTimer
|| !aTimer
->mHolder
) {
714 aTimer
->mHolder
->Forget(aTimer
);
718 void TimerThread::RemoveLeadingCanceledTimersInternal() {
719 mMonitor
.AssertCurrentThreadOwns();
721 // Move all canceled timers from the front of the list to
722 // the back of the list using std::pop_heap(). We do this
723 // without actually removing them from the list so we can
724 // modify the nsTArray in a single bulk operation.
725 auto sortedEnd
= mTimers
.end();
726 while (sortedEnd
!= mTimers
.begin() && !mTimers
[0]->Value()) {
727 std::pop_heap(mTimers
.begin(), sortedEnd
, Entry::UniquePtrLessThan
);
731 // If there were no canceled timers then we are done.
732 if (sortedEnd
== mTimers
.end()) {
736 // Finally, remove the canceled timers from the back of the
738 mTimers
.RemoveLastElements(mTimers
.end() - sortedEnd
);
741 void TimerThread::RemoveFirstTimerInternal() {
742 mMonitor
.AssertCurrentThreadOwns();
743 MOZ_ASSERT(!mTimers
.IsEmpty());
744 std::pop_heap(mTimers
.begin(), mTimers
.end(), Entry::UniquePtrLessThan
);
745 mTimers
.RemoveLastElement();
748 void TimerThread::PostTimerEvent(already_AddRefed
<nsTimerImpl
> aTimerRef
) {
749 mMonitor
.AssertCurrentThreadOwns();
751 RefPtr
<nsTimerImpl
> timer(aTimerRef
);
752 if (!timer
->mEventTarget
) {
753 NS_ERROR("Attempt to post timer event to NULL event target");
757 // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
759 // Since we already addref'd 'timer', we don't need to addref here.
760 // We will release either in ~nsTimerEvent(), or pass the reference back to
761 // the caller. We need to copy the generation number from this timer into the
762 // event, so we can avoid firing a timer that was re-initialized after being
765 nsCOMPtr
<nsIEventTarget
> target
= timer
->mEventTarget
;
767 void* p
= nsTimerEvent::operator new(sizeof(nsTimerEvent
));
771 RefPtr
<nsTimerEvent
> event
=
772 ::new (KnownNotNull
, p
) nsTimerEvent(timer
.forget(), mProfilerThreadId
);
776 // We release mMonitor around the Dispatch because if the Dispatch interacts
777 // with the timer API we'll deadlock.
778 MonitorAutoUnlock
unlock(mMonitor
);
779 rv
= target
->Dispatch(event
, NS_DISPATCH_NORMAL
);
781 timer
= event
->ForgetTimer();
782 // We do this to avoid possible deadlock by taking the two locks in a
783 // different order than is used in RemoveTimer(). RemoveTimer() has
784 // aTimer->mMutex first. We use timer.get() to keep static analysis
786 MutexAutoLock
lock1(timer
.get()->mMutex
);
787 MonitorAutoLock
lock2(mMonitor
);
788 RemoveTimerInternal(timer
.get());
793 void TimerThread::DoBeforeSleep() {
795 MonitorAutoLock
lock(mMonitor
);
799 // Note: wake may be notified without preceding sleep notification
800 void TimerThread::DoAfterSleep() {
802 MonitorAutoLock
lock(mMonitor
);
805 // Wake up the timer thread to re-process the array to ensure the sleep delay
806 // is correct, and fire any expired timers (perhaps quite a few)
808 PROFILER_MARKER_UNTYPED("AfterSleep", OTHER
,
809 MarkerThreadId(mProfilerThreadId
));
814 TimerThread::Observe(nsISupports
* /* aSubject */, const char* aTopic
,
815 const char16_t
* /* aData */) {
816 if (StaticPrefs::timer_ignore_sleep_wake_notifications()) {
820 if (strcmp(aTopic
, "sleep_notification") == 0 ||
821 strcmp(aTopic
, "suspend_process_notification") == 0) {
823 } else if (strcmp(aTopic
, "wake_notification") == 0 ||
824 strcmp(aTopic
, "resume_process_notification") == 0) {
831 uint32_t TimerThread::AllowedEarlyFiringMicroseconds() {
832 MonitorAutoLock
lock(mMonitor
);
833 return mAllowedEarlyFiringMicroseconds
;