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