| /* |
| * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/platform/timer.h" |
| |
| #include <limits.h> |
| #include <math.h> |
| #include <algorithm> |
| #include <limits> |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" |
| #include "third_party/blink/renderer/platform/wtf/address_sanitizer.h" |
| #include "third_party/blink/renderer/platform/wtf/atomics.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_set.h" |
| #include "third_party/blink/renderer/platform/wtf/time.h" |
| |
| namespace blink { |
| |
| TimerBase::TimerBase( |
| scoped_refptr<base::SingleThreadTaskRunner> web_task_runner) |
| : web_task_runner_(std::move(web_task_runner)), |
| #if DCHECK_IS_ON() |
| thread_(CurrentThread()), |
| #endif |
| weak_ptr_factory_(this) { |
| } |
| |
| TimerBase::~TimerBase() { |
| Stop(); |
| } |
| |
| void TimerBase::Start(TimeDelta next_fire_interval, |
| TimeDelta repeat_interval, |
| const base::Location& caller) { |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(thread_, CurrentThread()); |
| #endif |
| |
| location_ = caller; |
| repeat_interval_ = repeat_interval; |
| SetNextFireTime(TimerCurrentTimeTicks(), next_fire_interval); |
| } |
| |
| void TimerBase::Stop() { |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(thread_, CurrentThread()); |
| #endif |
| |
| repeat_interval_ = TimeDelta(); |
| next_fire_time_ = TimeTicks(); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| } |
| |
| TimeDelta TimerBase::NextFireIntervalDelta() const { |
| DCHECK(IsActive()); |
| TimeTicks current = TimerCurrentTimeTicks(); |
| if (next_fire_time_ < current) |
| return TimeDelta(); |
| return next_fire_time_ - current; |
| } |
| |
| void TimerBase::MoveToNewTaskRunner( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(thread_, CurrentThread()); |
| DCHECK(task_runner->RunsTasksInCurrentSequence()); |
| #endif |
| // If the underlying task runner stays the same, ignore it. |
| if (web_task_runner_ == task_runner) { |
| return; |
| } |
| |
| bool active = IsActive(); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| web_task_runner_ = std::move(task_runner); |
| |
| if (!active) |
| return; |
| |
| TimeTicks now = TimerCurrentTimeTicks(); |
| TimeTicks next_fire_time = std::max(next_fire_time_, now); |
| next_fire_time_ = TimeTicks(); |
| |
| SetNextFireTime(now, next_fire_time - now); |
| } |
| |
| scoped_refptr<base::SingleThreadTaskRunner> TimerBase::TimerTaskRunner() const { |
| return web_task_runner_; |
| } |
| |
| void TimerBase::SetNextFireTime(TimeTicks now, TimeDelta delay) { |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(thread_, CurrentThread()); |
| #endif |
| |
| TimeTicks new_time = now + delay; |
| |
| if (next_fire_time_ != new_time) { |
| next_fire_time_ = new_time; |
| |
| // Cancel any previously posted task. |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| |
| TimerTaskRunner()->PostDelayedTask( |
| location_, |
| WTF::Bind(&TimerBase::RunInternal, weak_ptr_factory_.GetWeakPtr()), |
| delay); |
| } |
| } |
| |
| NO_SANITIZE_ADDRESS |
| void TimerBase::RunInternal() { |
| if (!CanFire()) |
| return; |
| |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| |
| TRACE_EVENT0("blink", "TimerBase::run"); |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(thread_, CurrentThread()) |
| << "Timer posted by " << location_.function_name() << " " |
| << location_.file_name() << " was run on a different thread"; |
| #endif |
| |
| if (!repeat_interval_.is_zero()) { |
| TimeTicks now = TimerCurrentTimeTicks(); |
| // This computation should be drift free, and it will cope if we miss a |
| // beat, which can easily happen if the thread is busy. It will also cope |
| // if we get called slightly before m_unalignedNextFireTime, which can |
| // happen due to lack of timer precision. |
| TimeDelta interval_to_next_fire_time = |
| repeat_interval_ - (now - next_fire_time_) % repeat_interval_; |
| SetNextFireTime(now, interval_to_next_fire_time); |
| } else { |
| next_fire_time_ = TimeTicks(); |
| } |
| Fired(); |
| } |
| |
| bool TimerBase::Comparator::operator()(const TimerBase* a, |
| const TimerBase* b) const { |
| return a->next_fire_time_ < b->next_fire_time_; |
| } |
| |
| // static |
| TimeTicks TimerBase::TimerCurrentTimeTicks() const { |
| return WTF::TimeTicks(Platform::Current() |
| ->CurrentThread() |
| ->Scheduler() |
| ->MonotonicallyIncreasingVirtualTime()); |
| } |
| |
| } // namespace blink |