diff options
author | alexclarke <alexclarke@chromium.org> | 2015-11-27 02:08:42 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-27 10:09:37 +0000 |
commit | ec5adec0a9879a31866e98c65ddc7b506b9f49c3 (patch) | |
tree | 349ebd7c25c796c03a562467553caf22c2e17724 | |
parent | 73be357897b5496f6bfa8d5ddefbccfca24b7c62 (diff) | |
download | chromium_src-ec5adec0a9879a31866e98c65ddc7b506b9f49c3.zip chromium_src-ec5adec0a9879a31866e98c65ddc7b506b9f49c3.tar.gz chromium_src-ec5adec0a9879a31866e98c65ddc7b506b9f49c3.tar.bz2 |
Move throttling of background timers into the renderer scheduler
Not only does this simplify the code, it's more efficent since
previously setting the timer alignment resulted in mass cancellation
and reposting of timers.
BUG=510398, 546953, 560402
Review URL: https://codereview.chromium.org/1441073006
Cr-Commit-Position: refs/heads/master@{#361971}
55 files changed, 838 insertions, 493 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 14194f6..2b9f6c9 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -619,6 +619,7 @@ 'scheduler/renderer/renderer_scheduler_impl_unittest.cc', 'scheduler/renderer/render_widget_signals_unittest.cpp', 'scheduler/renderer/task_cost_estimator_unittest.cc', + 'scheduler/renderer/throttling_helper_unittest.cc', 'scheduler/renderer/user_model_unittest.cc', 'scheduler/renderer/web_view_scheduler_impl_unittest.cc', 'scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc', diff --git a/components/scheduler/BUILD.gn b/components/scheduler/BUILD.gn index 5031a61..c1070f1 100644 --- a/components/scheduler/BUILD.gn +++ b/components/scheduler/BUILD.gn @@ -62,6 +62,7 @@ source_set("unit_tests") { "renderer/render_widget_signals_unittest.cpp", "renderer/renderer_scheduler_impl_unittest.cc", "renderer/task_cost_estimator_unittest.cc", + "renderer/throttling_helper_unittest.cc", "renderer/user_model_unittest.cc", "renderer/web_view_scheduler_impl_unittest.cc", "renderer/webthread_impl_for_renderer_scheduler_unittest.cc", diff --git a/components/scheduler/base/real_time_domain.cc b/components/scheduler/base/real_time_domain.cc index c3b4505..d894eca 100644 --- a/components/scheduler/base/real_time_domain.cc +++ b/components/scheduler/base/real_time_domain.cc @@ -10,7 +10,7 @@ namespace scheduler { -RealTimeDomain::RealTimeDomain() : weak_factory_(this) {} +RealTimeDomain::RealTimeDomain() : TimeDomain(nullptr), weak_factory_(this) {} RealTimeDomain::~RealTimeDomain() {} diff --git a/components/scheduler/base/real_time_domain.h b/components/scheduler/base/real_time_domain.h index f382fad..8252563 100644 --- a/components/scheduler/base/real_time_domain.h +++ b/components/scheduler/base/real_time_domain.h @@ -17,6 +17,7 @@ class TaskQueueManagerDelegate; class SCHEDULER_EXPORT RealTimeDomain : public TimeDomain { public: RealTimeDomain(); + ~RealTimeDomain() override; // TimeDomain implementation: LazyNow CreateLazyNow() override; @@ -40,8 +41,6 @@ class SCHEDULER_EXPORT RealTimeDomain : public TimeDomain { base::Closure do_work_closure_; base::WeakPtrFactory<RealTimeDomain> weak_factory_; - ~RealTimeDomain() override; - DISALLOW_COPY_AND_ASSIGN(RealTimeDomain); }; diff --git a/components/scheduler/base/task_queue.cc b/components/scheduler/base/task_queue.cc index 8578605..24aadb7 100644 --- a/components/scheduler/base/task_queue.cc +++ b/components/scheduler/base/task_queue.cc @@ -6,8 +6,9 @@ namespace scheduler { -bool TaskQueue::IsQueueEmpty() const { - return GetQueueState() == QueueState::EMPTY; +bool TaskQueue::HasPendingImmediateTask() const { + QueueState state = GetQueueState(); + return state == QueueState::NEEDS_PUMPING || state == QueueState::HAS_WORK; } } // namespace scheduler diff --git a/components/scheduler/base/task_queue.h b/components/scheduler/base/task_queue.h index 393cc50..fbbbc25 100644 --- a/components/scheduler/base/task_queue.h +++ b/components/scheduler/base/task_queue.h @@ -20,12 +20,6 @@ class SCHEDULER_EXPORT TaskQueue : public base::SingleThreadTaskRunner { // the TaskQueueManager's reference to it will be released soon. virtual void UnregisterTaskQueue() = 0; - // Post a delayed task at an absolute desired run time instead of a time - // delta from the current time. - virtual bool PostDelayedTaskAt(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeTicks desired_run_time) = 0; - enum QueuePriority { // Queues with control priority will run before any other queue, and will // explicitly starve other queues. Typically this should only be used for @@ -91,6 +85,9 @@ class SCHEDULER_EXPORT TaskQueue : public base::SingleThreadTaskRunner { // A queue in the HAS_WORK state has tasks in the work task queue which // are runnable. HAS_WORK, + // The work and incomming queues are empty but there is delayed work + // scheduled. + NO_IMMEDIATE_WORK, }; // Options for constructing a TaskQueue. Once set the |name|, @@ -145,10 +142,11 @@ class SCHEDULER_EXPORT TaskQueue : public base::SingleThreadTaskRunner { virtual bool IsQueueEnabled() const = 0; // Returns true if there no tasks in either the work or incoming task queue. + // This method ignores delayed tasks that are scheduled to run in the future. // Note that this function involves taking a lock, so calling it has some // overhead. NOTE this must be called on the thread this TaskQueue was created // by. - virtual bool IsQueueEmpty() const; + virtual bool HasPendingImmediateTask() const; // Returns the QueueState. Note that this function involves taking a lock, so // calling it has some overhead. @@ -183,7 +181,7 @@ class SCHEDULER_EXPORT TaskQueue : public base::SingleThreadTaskRunner { // Removes the task queue from the previous TimeDomain and adds it to // |domain|. This is a moderately expensive operation. - virtual void SetTimeDomain(const scoped_refptr<TimeDomain>& domain) = 0; + virtual void SetTimeDomain(TimeDomain* domain) = 0; protected: ~TaskQueue() override {} diff --git a/components/scheduler/base/task_queue_impl.cc b/components/scheduler/base/task_queue_impl.cc index 0ad965b..14b6c31 100644 --- a/components/scheduler/base/task_queue_impl.cc +++ b/components/scheduler/base/task_queue_impl.cc @@ -13,7 +13,7 @@ namespace internal { TaskQueueImpl::TaskQueueImpl( TaskQueueManager* task_queue_manager, - const scoped_refptr<TimeDomain>& time_domain, + TimeDomain* time_domain, const Spec& spec, const char* disabled_by_default_tracing_category, const char* disabled_by_default_verbose_tracing_category) @@ -28,10 +28,15 @@ TaskQueueImpl::TaskQueueImpl( wakeup_policy_(spec.wakeup_policy), should_monitor_quiescence_(spec.should_monitor_quiescence), should_notify_observers_(spec.should_notify_observers) { - DCHECK(time_domain.get()); + DCHECK(time_domain); + time_domain->RegisterQueue(this); } -TaskQueueImpl::~TaskQueueImpl() {} +TaskQueueImpl::~TaskQueueImpl() { + base::AutoLock lock(any_thread_lock_); + if (any_thread().time_domain) + any_thread().time_domain->UnregisterQueue(this); +} TaskQueueImpl::Task::Task() : PendingTask(tracked_objects::Location(), @@ -57,10 +62,9 @@ TaskQueueImpl::Task::Task(const tracked_objects::Location& posted_from, sequence_num = sequence_number; } -TaskQueueImpl::AnyThread::AnyThread( - TaskQueueManager* task_queue_manager, - PumpPolicy pump_policy, - const scoped_refptr<TimeDomain>& time_domain) +TaskQueueImpl::AnyThread::AnyThread(TaskQueueManager* task_queue_manager, + PumpPolicy pump_policy, + TimeDomain* time_domain) : task_queue_manager(task_queue_manager), pump_policy(pump_policy), time_domain(time_domain) {} @@ -78,7 +82,8 @@ void TaskQueueImpl::UnregisterTaskQueue() { base::AutoLock lock(any_thread_lock_); if (!any_thread().task_queue_manager) return; - any_thread().time_domain->UnregisterQueue(this); + if (any_thread().time_domain) + any_thread().time_domain->UnregisterQueue(this); any_thread().time_domain = nullptr; any_thread().task_queue_manager->UnregisterTaskQueue(this); @@ -107,18 +112,6 @@ bool TaskQueueImpl::PostNonNestableDelayedTask( return PostDelayedTaskImpl(from_here, task, delay, TaskType::NON_NESTABLE); } -bool TaskQueueImpl::PostDelayedTaskAt( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeTicks desired_run_time) { - base::AutoLock lock(any_thread_lock_); - if (!any_thread().task_queue_manager) - return false; - LazyNow lazy_now(any_thread().time_domain->CreateLazyNow()); - return PostDelayedTaskLocked(&lazy_now, from_here, task, desired_run_time, - TaskType::NORMAL); -} - bool TaskQueueImpl::PostDelayedTaskImpl( const tracked_objects::Location& from_here, const base::Closure& task, @@ -176,9 +169,8 @@ bool TaskQueueImpl::PostDelayedTaskLocked( return true; } -void TaskQueueImpl::ScheduleDelayedWorkTask( - const scoped_refptr<TimeDomain> time_domain, - base::TimeTicks desired_run_time) { +void TaskQueueImpl::ScheduleDelayedWorkTask(TimeDomain* time_domain, + base::TimeTicks desired_run_time) { DCHECK(main_thread_checker_.CalledOnValidThread()); LazyNow lazy_now(time_domain->CreateLazyNow()); time_domain->ScheduleDelayedWork(this, desired_run_time, &lazy_now); @@ -219,13 +211,15 @@ TaskQueue::QueueState TaskQueueImpl::GetQueueState() const { { base::AutoLock lock(any_thread_lock_); if (any_thread().incoming_queue.empty()) { - return QueueState::EMPTY; + if (any_thread().delayed_task_queue.empty()) + return QueueState::EMPTY; + else + return QueueState::NO_IMMEDIATE_WORK; } else { return QueueState::NEEDS_PUMPING; } } } - bool TaskQueueImpl::TaskIsOlderThanQueuedTasks(const Task* task) { // A null task is passed when UpdateQueue is called before any task is run. // In this case we don't want to pump an after_wakeup queue, so return true @@ -505,14 +499,14 @@ void TaskQueueImpl::NotifyDidProcessTask( DidProcessTask(pending_task)); } -void TaskQueueImpl::SetTimeDomain( - const scoped_refptr<TimeDomain>& time_domain) { +void TaskQueueImpl::SetTimeDomain(TimeDomain* time_domain) { base::AutoLock lock(any_thread_lock_); DCHECK(main_thread_checker_.CalledOnValidThread()); if (time_domain == any_thread().time_domain) return; - any_thread().time_domain->MigrateQueue(this, time_domain.get()); + if (time_domain) + any_thread().time_domain->MigrateQueue(this, time_domain); any_thread().time_domain = time_domain; } diff --git a/components/scheduler/base/task_queue_impl.h b/components/scheduler/base/task_queue_impl.h index 1f130e124..aa84639 100644 --- a/components/scheduler/base/task_queue_impl.h +++ b/components/scheduler/base/task_queue_impl.h @@ -23,7 +23,7 @@ namespace internal { class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { public: TaskQueueImpl(TaskQueueManager* task_queue_manager, - const scoped_refptr<TimeDomain>& time_domain, + TimeDomain* time_domain, const Spec& spec, const char* disabled_by_default_tracing_category, const char* disabled_by_default_verbose_tracing_category); @@ -70,9 +70,6 @@ class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta delay) override; - bool PostDelayedTaskAt(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeTicks desired_run_time) override; bool IsQueueEnabled() const override; QueueState GetQueueState() const override; @@ -82,7 +79,7 @@ class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override; void RemoveTaskObserver( base::MessageLoop::TaskObserver* task_observer) override; - void SetTimeDomain(const scoped_refptr<TimeDomain>& time_domain) override; + void SetTimeDomain(TimeDomain* time_domain) override; void UpdateWorkQueue(LazyNow* lazy_now, bool should_trigger_wakeup, @@ -154,7 +151,7 @@ class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { struct AnyThread { AnyThread(TaskQueueManager* task_queue_manager, PumpPolicy pump_policy, - const scoped_refptr<TimeDomain>& time_domain); + TimeDomain* time_domain); ~AnyThread(); // TaskQueueManager is maintained in two copies: inside AnyThread and inside @@ -165,7 +162,7 @@ class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { std::queue<Task> incoming_queue; PumpPolicy pump_policy; std::priority_queue<Task> delayed_task_queue; - scoped_refptr<TimeDomain> time_domain; + TimeDomain* time_domain; }; struct MainThreadOnly { @@ -192,7 +189,7 @@ class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { const base::Closure& task, base::TimeTicks desired_run_time, TaskType task_type); - void ScheduleDelayedWorkTask(const scoped_refptr<TimeDomain> time_domain, + void ScheduleDelayedWorkTask(TimeDomain* time_domain, base::TimeTicks desired_run_time); // Enqueues any delayed tasks which should be run now on the incoming_queue_. diff --git a/components/scheduler/base/task_queue_manager.cc b/components/scheduler/base/task_queue_manager.cc index b9df875..ec478db 100644 --- a/components/scheduler/base/task_queue_manager.cc +++ b/components/scheduler/base/task_queue_manager.cc @@ -21,7 +21,8 @@ TaskQueueManager::TaskQueueManager( const char* tracing_category, const char* disabled_by_default_tracing_category, const char* disabled_by_default_verbose_tracing_category) - : delegate_(delegate), + : real_time_domain_(new RealTimeDomain()), + delegate_(delegate), task_was_run_on_quiescence_monitored_queue_(false), pending_dowork_count_(0), work_batch_size_(1), @@ -44,8 +45,7 @@ TaskQueueManager::TaskQueueManager( base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), false); // TODO(alexclarke): Change this to be a parameter that's passed in. - real_time_domain_ = make_scoped_refptr(new RealTimeDomain()); - RegisterTimeDomain(real_time_domain_); + RegisterTimeDomain(real_time_domain_.get()); } TaskQueueManager::~TaskQueueManager() { @@ -58,15 +58,13 @@ TaskQueueManager::~TaskQueueManager() { selector_.SetTaskQueueSelectorObserver(nullptr); } -void TaskQueueManager::RegisterTimeDomain( - const scoped_refptr<TimeDomain>& time_domain) { +void TaskQueueManager::RegisterTimeDomain(TimeDomain* time_domain) { time_domains_.insert(time_domain); time_domain->OnRegisterWithTaskQueueManager(delegate_.get(), do_work_closure_); } -void TaskQueueManager::UnregisterTimeDomain( - const scoped_refptr<TimeDomain>& time_domain) { +void TaskQueueManager::UnregisterTimeDomain(TimeDomain* time_domain) { time_domains_.erase(time_domain); } @@ -77,8 +75,7 @@ scoped_refptr<internal::TaskQueueImpl> TaskQueueManager::NewTaskQueue( DCHECK(main_thread_checker_.CalledOnValidThread()); TimeDomain* time_domain = spec.time_domain ? spec.time_domain : real_time_domain_.get(); - DCHECK(time_domains_.find(make_scoped_refptr(time_domain)) != - time_domains_.end()); + DCHECK(time_domains_.find(time_domain) != time_domains_.end()); scoped_refptr<internal::TaskQueueImpl> queue( make_scoped_refptr(new internal::TaskQueueImpl( this, time_domain, spec, disabled_by_default_tracing_category_, @@ -115,7 +112,7 @@ void TaskQueueManager::UpdateWorkQueues( TRACE_EVENT0(disabled_by_default_tracing_category_, "TaskQueueManager::UpdateWorkQueues"); - for (const scoped_refptr<TimeDomain>& time_domain : time_domains_) { + for (TimeDomain* time_domain : time_domains_) { time_domain->UpdateWorkQueues(should_trigger_wakeup, previous_task); } } @@ -189,7 +186,7 @@ void TaskQueueManager::DoWork(bool decrement_pending_dowork_count) { bool TaskQueueManager::TryAdvanceTimeDomains() { bool can_advance = false; - for (const scoped_refptr<TimeDomain>& time_domain : time_domains_) { + for (TimeDomain* time_domain : time_domains_) { can_advance |= time_domain->MaybeAdvanceTime(); } return can_advance; diff --git a/components/scheduler/base/task_queue_manager.h b/components/scheduler/base/task_queue_manager.h index f6af854..f7090bd 100644 --- a/components/scheduler/base/task_queue_manager.h +++ b/components/scheduler/base/task_queue_manager.h @@ -100,12 +100,10 @@ class SCHEDULER_EXPORT TaskQueueManager const scoped_refptr<TaskQueueManagerDelegate>& delegate() const; // Time domains must be registered for the task queues to get updated. - void RegisterTimeDomain(const scoped_refptr<TimeDomain>& time_domain); - void UnregisterTimeDomain(const scoped_refptr<TimeDomain>& time_domain); + void RegisterTimeDomain(TimeDomain* time_domain); + void UnregisterTimeDomain(TimeDomain* time_domain); - const scoped_refptr<RealTimeDomain>& real_time_domain() const { - return real_time_domain_; - } + RealTimeDomain* real_time_domain() const { return real_time_domain_.get(); } private: friend class LazyNow; @@ -176,8 +174,8 @@ class SCHEDULER_EXPORT TaskQueueManager AsValueWithSelectorResult(bool should_run, internal::TaskQueueImpl* selected_queue) const; - std::set<scoped_refptr<TimeDomain>> time_domains_; - scoped_refptr<RealTimeDomain> real_time_domain_; + std::set<TimeDomain*> time_domains_; + scoped_ptr<RealTimeDomain> real_time_domain_; std::set<scoped_refptr<internal::TaskQueueImpl>> queues_; @@ -212,7 +210,6 @@ class SCHEDULER_EXPORT TaskQueueManager Observer* observer_; // NOT OWNED scoped_refptr<DeletionSentinel> deletion_sentinel_; - scoped_refptr<TimeDomain> time_domain_; base::WeakPtrFactory<TaskQueueManager> weak_factory_; DISALLOW_COPY_AND_ASSIGN(TaskQueueManager); diff --git a/components/scheduler/base/task_queue_manager_unittest.cc b/components/scheduler/base/task_queue_manager_unittest.cc index 251ac56..f1d9dc4 100644 --- a/components/scheduler/base/task_queue_manager_unittest.cc +++ b/components/scheduler/base/task_queue_manager_unittest.cc @@ -216,12 +216,12 @@ TEST_F(TaskQueueManagerTest, QueuePolling) { Initialize(1u); std::vector<int> run_order; - EXPECT_TRUE(runners_[0]->IsQueueEmpty()); + EXPECT_FALSE(runners_[0]->HasPendingImmediateTask()); runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - EXPECT_FALSE(runners_[0]->IsQueueEmpty()); + EXPECT_TRUE(runners_[0]->HasPendingImmediateTask()); test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(runners_[0]->IsQueueEmpty()); + EXPECT_FALSE(runners_[0]->HasPendingImmediateTask()); } TEST_F(TaskQueueManagerTest, DelayedTaskPosting) { @@ -232,7 +232,8 @@ TEST_F(TaskQueueManagerTest, DelayedTaskPosting) { runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), delay); EXPECT_EQ(delay, test_task_runner_->DelayToNextTaskTime()); - EXPECT_TRUE(runners_[0]->IsQueueEmpty()); + EXPECT_EQ(TaskQueue::QueueState::NO_IMMEDIATE_WORK, + runners_[0]->GetQueueState()); EXPECT_TRUE(run_order.empty()); // The task doesn't run before the delay has completed. @@ -242,6 +243,7 @@ TEST_F(TaskQueueManagerTest, DelayedTaskPosting) { // After the delay has completed, the task runs normally. test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); EXPECT_THAT(run_order, ElementsAre(1)); + EXPECT_EQ(TaskQueue::QueueState::EMPTY, runners_[0]->GetQueueState()); } bool MessageLoopTaskCounter(size_t* count) { @@ -375,7 +377,7 @@ TEST_F(TaskQueueManagerTest, ManualPumping) { EXPECT_FALSE(test_task_runner_->HasPendingTasks()); // However polling still works. - EXPECT_FALSE(runners_[0]->IsQueueEmpty()); + EXPECT_TRUE(runners_[0]->HasPendingImmediateTask()); // After pumping the task runs normally. runners_[0]->PumpQueue(); @@ -1003,32 +1005,32 @@ TEST_F(TaskQueueManagerTest, GetAndClearSystemIsQuiescentBit) { EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); } -TEST_F(TaskQueueManagerTest, IsQueueEmpty) { +TEST_F(TaskQueueManagerTest, HasPendingImmediateTask) { Initialize(2u); internal::TaskQueueImpl* queue0 = runners_[0].get(); internal::TaskQueueImpl* queue1 = runners_[1].get(); queue0->SetPumpPolicy(TaskQueue::PumpPolicy::AUTO); queue1->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - EXPECT_TRUE(queue0->IsQueueEmpty()); - EXPECT_TRUE(queue1->IsQueueEmpty()); + EXPECT_FALSE(queue0->HasPendingImmediateTask()); + EXPECT_FALSE(queue1->HasPendingImmediateTask()); queue0->PostTask(FROM_HERE, base::Bind(NullTask)); queue1->PostTask(FROM_HERE, base::Bind(NullTask)); - EXPECT_FALSE(queue0->IsQueueEmpty()); - EXPECT_FALSE(queue1->IsQueueEmpty()); + EXPECT_TRUE(queue0->HasPendingImmediateTask()); + EXPECT_TRUE(queue1->HasPendingImmediateTask()); test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(queue0->IsQueueEmpty()); - EXPECT_FALSE(queue1->IsQueueEmpty()); + EXPECT_FALSE(queue0->HasPendingImmediateTask()); + EXPECT_TRUE(queue1->HasPendingImmediateTask()); queue1->PumpQueue(); - EXPECT_TRUE(queue0->IsQueueEmpty()); - EXPECT_FALSE(queue1->IsQueueEmpty()); + EXPECT_FALSE(queue0->HasPendingImmediateTask()); + EXPECT_TRUE(queue1->HasPendingImmediateTask()); test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(queue0->IsQueueEmpty()); - EXPECT_TRUE(queue1->IsQueueEmpty()); + EXPECT_FALSE(queue0->HasPendingImmediateTask()); + EXPECT_FALSE(queue1->HasPendingImmediateTask()); } TEST_F(TaskQueueManagerTest, GetQueueState) { @@ -1102,33 +1104,6 @@ TEST_F(TaskQueueManagerTest, DelayedTaskDoesNotSkipAHeadOfShorterDelayedTask) { EXPECT_THAT(run_order, ElementsAre(2, 1)); } -TEST_F(TaskQueueManagerTest, DelayedTaskWithAbsoluteRunTime) { - Initialize(1u); - - // One task in the past, two with the exact same run time and one in the - // future. - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks runTime1 = now_src_->NowTicks() - delay; - base::TimeTicks runTime2 = now_src_->NowTicks(); - base::TimeTicks runTime3 = now_src_->NowTicks(); - base::TimeTicks runTime4 = now_src_->NowTicks() + delay; - - std::vector<int> run_order; - runners_[0]->PostDelayedTaskAt( - FROM_HERE, base::Bind(&TestTask, 1, &run_order), runTime1); - runners_[0]->PostDelayedTaskAt( - FROM_HERE, base::Bind(&TestTask, 2, &run_order), runTime2); - runners_[0]->PostDelayedTaskAt( - FROM_HERE, base::Bind(&TestTask, 3, &run_order), runTime3); - runners_[0]->PostDelayedTaskAt( - FROM_HERE, base::Bind(&TestTask, 4, &run_order), runTime4); - - now_src_->Advance(2 * delay); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); -} - void CheckIsNested(bool* is_nested) { *is_nested = base::MessageLoop::current()->IsNested(); } @@ -1323,20 +1298,6 @@ TEST_F(TaskQueueManagerTest, OnUnregisterTaskQueue) { manager_->SetObserver(nullptr); } -TEST_F(TaskQueueManagerTest, ScheduleDelayedWorkIsNotReEntrant) { - Initialize(1u); - - // Post two tasks into the past. The second one used to trigger a deadlock - // because it tried to re-entrantly wake the first task in the same queue. - runners_[0]->PostDelayedTaskAt( - FROM_HERE, base::Bind(&NullTask), - base::TimeTicks() + base::TimeDelta::FromMicroseconds(100)); - runners_[0]->PostDelayedTaskAt( - FROM_HERE, base::Bind(&NullTask), - base::TimeTicks() + base::TimeDelta::FromMicroseconds(200)); - test_task_runner_->RunUntilIdle(); -} - void HasOneRefTask(std::vector<bool>* log, internal::TaskQueueImpl* tq) { log->push_back(tq->HasOneRef()); } @@ -1387,12 +1348,14 @@ TEST_F(TaskQueueManagerTest, TimeDomainsAreIndependant) { Initialize(2u); base::TimeTicks start_time = manager_->delegate()->NowTicks(); - scoped_refptr<VirtualTimeDomain> domain_a(new VirtualTimeDomain(start_time)); - scoped_refptr<VirtualTimeDomain> domain_b(new VirtualTimeDomain(start_time)); - manager_->RegisterTimeDomain(domain_a); - manager_->RegisterTimeDomain(domain_b); - runners_[0]->SetTimeDomain(domain_a); - runners_[1]->SetTimeDomain(domain_b); + scoped_ptr<VirtualTimeDomain> domain_a( + new VirtualTimeDomain(nullptr, start_time)); + scoped_ptr<VirtualTimeDomain> domain_b( + new VirtualTimeDomain(nullptr, start_time)); + manager_->RegisterTimeDomain(domain_a.get()); + manager_->RegisterTimeDomain(domain_b.get()); + runners_[0]->SetTimeDomain(domain_a.get()); + runners_[1]->SetTimeDomain(domain_b.get()); std::vector<int> run_order; runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), @@ -1419,17 +1382,18 @@ TEST_F(TaskQueueManagerTest, TimeDomainsAreIndependant) { test_task_runner_->RunUntilIdle(); EXPECT_THAT(run_order, ElementsAre(4, 5, 6, 1, 2, 3)); - manager_->UnregisterTimeDomain(domain_a); - manager_->UnregisterTimeDomain(domain_b); + manager_->UnregisterTimeDomain(domain_a.get()); + manager_->UnregisterTimeDomain(domain_b.get()); } TEST_F(TaskQueueManagerTest, TimeDomainMigration) { Initialize(1u); base::TimeTicks start_time = manager_->delegate()->NowTicks(); - scoped_refptr<VirtualTimeDomain> domain_a(new VirtualTimeDomain(start_time)); - manager_->RegisterTimeDomain(domain_a); - runners_[0]->SetTimeDomain(domain_a); + scoped_ptr<VirtualTimeDomain> domain_a( + new VirtualTimeDomain(nullptr, start_time)); + manager_->RegisterTimeDomain(domain_a.get()); + runners_[0]->SetTimeDomain(domain_a.get()); std::vector<int> run_order; runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), @@ -1445,17 +1409,18 @@ TEST_F(TaskQueueManagerTest, TimeDomainMigration) { test_task_runner_->RunUntilIdle(); EXPECT_THAT(run_order, ElementsAre(1, 2)); - scoped_refptr<VirtualTimeDomain> domain_b(new VirtualTimeDomain(start_time)); - manager_->RegisterTimeDomain(domain_b); - runners_[0]->SetTimeDomain(domain_b); + scoped_ptr<VirtualTimeDomain> domain_b( + new VirtualTimeDomain(nullptr, start_time)); + manager_->RegisterTimeDomain(domain_b.get()); + runners_[0]->SetTimeDomain(domain_b.get()); domain_b->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50)); test_task_runner_->RunUntilIdle(); EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); - manager_->UnregisterTimeDomain(domain_a); - manager_->UnregisterTimeDomain(domain_b); + manager_->UnregisterTimeDomain(domain_a.get()); + manager_->UnregisterTimeDomain(domain_b.get()); } namespace { diff --git a/components/scheduler/base/task_queue_selector_unittest.cc b/components/scheduler/base/task_queue_selector_unittest.cc index 382a440..da64fac 100644 --- a/components/scheduler/base/task_queue_selector_unittest.cc +++ b/components/scheduler/base/task_queue_selector_unittest.cc @@ -75,11 +75,11 @@ class TaskQueueSelectorTest : public testing::Test { protected: void SetUp() final { - virtual_time_domain_ = make_scoped_refptr<VirtualTimeDomain>( - new VirtualTimeDomain(base::TimeTicks())); + virtual_time_domain_ = make_scoped_ptr<VirtualTimeDomain>( + new VirtualTimeDomain(nullptr, base::TimeTicks())); for (size_t i = 0; i < kTaskQueueCount; i++) { scoped_refptr<TaskQueueImpl> task_queue = make_scoped_refptr( - new TaskQueueImpl(nullptr, virtual_time_domain_, + new TaskQueueImpl(nullptr, virtual_time_domain_.get(), TaskQueue::Spec("test queue"), "test", "test")); selector_.AddQueue(task_queue.get()); task_queues_.push_back(task_queue); @@ -93,7 +93,7 @@ class TaskQueueSelectorTest : public testing::Test { const size_t kTaskQueueCount = 5; base::Closure test_closure_; TaskQueueSelector selector_; - scoped_refptr<VirtualTimeDomain> virtual_time_domain_; + scoped_ptr<VirtualTimeDomain> virtual_time_domain_; std::vector<scoped_refptr<TaskQueueImpl>> task_queues_; std::map<TaskQueueImpl*, size_t> queue_to_index_map_; }; diff --git a/components/scheduler/base/task_queue_sets_unittest.cc b/components/scheduler/base/task_queue_sets_unittest.cc index 568c0d1..8d42503 100644 --- a/components/scheduler/base/task_queue_sets_unittest.cc +++ b/components/scheduler/base/task_queue_sets_unittest.cc @@ -16,8 +16,8 @@ namespace internal { class TaskQueueSetsTest : public testing::Test { public: void SetUp() override { - virtual_time_domain_ = make_scoped_refptr<VirtualTimeDomain>( - new VirtualTimeDomain(base::TimeTicks())); + virtual_time_domain_ = make_scoped_ptr<VirtualTimeDomain>( + new VirtualTimeDomain(nullptr, base::TimeTicks())); task_queue_sets_.reset(new TaskQueueSets(kNumSets)); } @@ -29,8 +29,8 @@ class TaskQueueSetsTest : public testing::Test { TaskQueueImpl* NewTaskQueue(const char* queue_name) { scoped_refptr<internal::TaskQueueImpl> queue = make_scoped_refptr(new internal::TaskQueueImpl( - nullptr, virtual_time_domain_, TaskQueue::Spec(queue_name), "test", - "test")); + nullptr, virtual_time_domain_.get(), TaskQueue::Spec(queue_name), + "test", "test")); task_queues_.push_back(queue); return queue.get(); } @@ -41,7 +41,7 @@ class TaskQueueSetsTest : public testing::Test { return fake_task; } - scoped_refptr<VirtualTimeDomain> virtual_time_domain_; + scoped_ptr<VirtualTimeDomain> virtual_time_domain_; std::vector<scoped_refptr<internal::TaskQueueImpl>> task_queues_; scoped_ptr<TaskQueueSets> task_queue_sets_; }; diff --git a/components/scheduler/base/time_domain.cc b/components/scheduler/base/time_domain.cc index 2abfb52..e64b4e9 100644 --- a/components/scheduler/base/time_domain.cc +++ b/components/scheduler/base/time_domain.cc @@ -12,11 +12,21 @@ namespace scheduler { -TimeDomain::TimeDomain() {} +TimeDomain::TimeDomain(Observer* observer) : observer_(observer) {} -TimeDomain::~TimeDomain() {} +TimeDomain::~TimeDomain() { + for (internal::TaskQueueImpl* queue : registered_task_queues_) { + queue->SetTimeDomain(nullptr); + } +} + +void TimeDomain::RegisterQueue(internal::TaskQueueImpl* queue) { + registered_task_queues_.insert(queue); +} void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) { + registered_task_queues_.erase(queue); + // We need to remove |task_queue| from delayed_wakeup_multimap_ which is a // little awkward since it's keyed by time. O(n) running time. for (DelayedWakeupMultimap::iterator iter = delayed_wakeup_multimap_.begin(); @@ -39,6 +49,9 @@ void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) { void TimeDomain::MigrateQueue(internal::TaskQueueImpl* queue, TimeDomain* destination_time_domain) { + DCHECK(destination_time_domain); + registered_task_queues_.erase(queue); + LazyNow destination_lazy_now = destination_time_domain->CreateLazyNow(); // We need to remove |task_queue| from delayed_wakeup_multimap_ which is a // little awkward since it's keyed by time. O(n) running time. @@ -60,6 +73,8 @@ void TimeDomain::MigrateQueue(internal::TaskQueueImpl* queue, // MoveNewlyUpdatableQueuesIntoUpdatableQueueSet to flush it out. MoveNewlyUpdatableQueuesIntoUpdatableQueueSet(); updatable_queue_set_.erase(queue); + + destination_time_domain->RegisterQueue(queue); } void TimeDomain::ScheduleDelayedWork(internal::TaskQueueImpl* queue, @@ -67,18 +82,26 @@ void TimeDomain::ScheduleDelayedWork(internal::TaskQueueImpl* queue, LazyNow* lazy_now) { DCHECK(main_thread_checker_.CalledOnValidThread()); - if (delayed_wakeup_multimap_.empty() || + bool delayed_wakeup_multimap_was_empty = delayed_wakeup_multimap_.empty(); + if (delayed_wakeup_multimap_was_empty || delayed_run_time < delayed_wakeup_multimap_.begin()->first) { base::TimeDelta delay = std::max(base::TimeDelta(), delayed_run_time - lazy_now->Now()); RequestWakeup(lazy_now, delay); } + + if (observer_ && delayed_wakeup_multimap_was_empty) + observer_->OnTimeDomainHasDelayedWork(); delayed_wakeup_multimap_.insert(std::make_pair(delayed_run_time, queue)); } void TimeDomain::RegisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue) { - base::AutoLock lock(newly_updatable_lock_); - newly_updatable_.push_back(queue); + { + base::AutoLock lock(newly_updatable_lock_); + newly_updatable_.push_back(queue); + } + if (observer_) + observer_->OnTimeDomainHasImmediateWork(); } void TimeDomain::UnregisterAsUpdatableTaskQueue( diff --git a/components/scheduler/base/time_domain.h b/components/scheduler/base/time_domain.h index 476a68e..5cf39b7 100644 --- a/components/scheduler/base/time_domain.h +++ b/components/scheduler/base/time_domain.h @@ -23,9 +23,24 @@ class TaskQueueImpl; class TaskQueueManager; class TaskQueueManagerDelegate; -class SCHEDULER_EXPORT TimeDomain : public base::RefCounted<TimeDomain> { +class SCHEDULER_EXPORT TimeDomain { public: - TimeDomain(); + class SCHEDULER_EXPORT Observer { + public: + virtual ~Observer() {} + + // Called when an empty TaskQueue registered with this TimeDomain has a task + // enqueued. + virtual void OnTimeDomainHasImmediateWork() = 0; + + // Called when a TaskQueue registered with this TimeDomain has a delayed + // task enqueued and no other delayed tasks associated with this TimeDomain + // are pending. + virtual void OnTimeDomainHasDelayedWork() = 0; + }; + + explicit TimeDomain(Observer* observer); + virtual ~TimeDomain(); // Returns a LazyNow that evaluate this TimeDomain's Now. Can be called from // any thread. @@ -46,9 +61,6 @@ class SCHEDULER_EXPORT TimeDomain : public base::RefCounted<TimeDomain> { protected: friend class internal::TaskQueueImpl; friend class TaskQueueManager; - friend class base::RefCounted<TimeDomain>; - - virtual ~TimeDomain(); void AsValueInto(base::trace_event::TracedValue* state) const; @@ -70,6 +82,9 @@ class SCHEDULER_EXPORT TimeDomain : public base::RefCounted<TimeDomain> { base::TimeTicks delayed_run_time, LazyNow* lazy_now); + // Registers the |queue|. + void RegisterQueue(internal::TaskQueueImpl* queue); + // Removes |queue| from the set of task queues that UpdateWorkQueues calls // UpdateWorkQueue on. void UnregisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue); @@ -115,6 +130,10 @@ class SCHEDULER_EXPORT TimeDomain : public base::RefCounted<TimeDomain> { // only be accessed from the main thread. std::set<internal::TaskQueueImpl*> updatable_queue_set_; + std::set<internal::TaskQueueImpl*> registered_task_queues_; + + Observer* observer_; + base::ThreadChecker main_thread_checker_; DISALLOW_COPY_AND_ASSIGN(TimeDomain); diff --git a/components/scheduler/base/time_domain_unittest.cc b/components/scheduler/base/time_domain_unittest.cc index d41305a..e699e18 100644 --- a/components/scheduler/base/time_domain_unittest.cc +++ b/components/scheduler/base/time_domain_unittest.cc @@ -19,14 +19,18 @@ namespace scheduler { class MockTimeDomain : public TimeDomain { public: - MockTimeDomain() - : now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {} + explicit MockTimeDomain(TimeDomain::Observer* observer) + : TimeDomain(observer), + now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {} + + ~MockTimeDomain() override {} using TimeDomain::NextScheduledRunTime; using TimeDomain::NextScheduledTaskQueue; using TimeDomain::ScheduleDelayedWork; using TimeDomain::UnregisterQueue; using TimeDomain::UpdateWorkQueues; + using TimeDomain::RegisterAsUpdatableTaskQueue; // TimeSource implementation: LazyNow CreateLazyNow() override { return LazyNow(now_); } @@ -49,21 +53,23 @@ class MockTimeDomain : public TimeDomain { private: base::TimeTicks now_; - ~MockTimeDomain() override {} - DISALLOW_COPY_AND_ASSIGN(MockTimeDomain); }; class TimeDomainTest : public testing::Test { public: void SetUp() final { - time_domain_ = make_scoped_refptr(new MockTimeDomain()); + time_domain_ = make_scoped_ptr(CreateMockTimeDomain()); task_queue_ = make_scoped_refptr(new internal::TaskQueueImpl( - nullptr, time_domain_, TaskQueue::Spec("test_queue"), "test.category", - "test.category")); + nullptr, time_domain_.get(), TaskQueue::Spec("test_queue"), + "test.category", "test.category")); + } + + virtual MockTimeDomain* CreateMockTimeDomain() { + return new MockTimeDomain(nullptr); } - scoped_refptr<MockTimeDomain> time_domain_; + scoped_ptr<MockTimeDomain> time_domain_; scoped_refptr<internal::TaskQueueImpl> task_queue_; }; @@ -115,7 +121,7 @@ TEST_F(TimeDomainTest, RequestWakeup_OnlyCalledForEarlierTasks) { TEST_F(TimeDomainTest, UnregisterQueue) { scoped_refptr<internal::TaskQueueImpl> task_queue2_ = make_scoped_refptr(new internal::TaskQueueImpl( - nullptr, time_domain_, TaskQueue::Spec("test_queue2"), + nullptr, time_domain_.get(), TaskQueue::Spec("test_queue2"), "test.category", "test.category")); EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(1); @@ -140,7 +146,7 @@ TEST_F(TimeDomainTest, UnregisterQueue) { } TEST_F(TimeDomainTest, UpdateWorkQueues) { - scoped_refptr<MockTimeDomain> dummy_delegate(new MockTimeDomain()); + scoped_ptr<MockTimeDomain> dummy_delegate(new MockTimeDomain(nullptr)); base::SimpleTestTickClock dummy_time_source; scoped_refptr<cc::OrderedSimpleTaskRunner> dummy_task_runner( new cc::OrderedSimpleTaskRunner(&dummy_time_source, false)); @@ -176,4 +182,38 @@ TEST_F(TimeDomainTest, UpdateWorkQueues) { EXPECT_EQ(1UL, dummy_queue->IncomingQueueSizeForTest()); } +namespace { +class MockObserver : public TimeDomain::Observer { + public: + ~MockObserver() override {} + + MOCK_METHOD0(OnTimeDomainHasImmediateWork, void()); + MOCK_METHOD0(OnTimeDomainHasDelayedWork, void()); +}; +} // namespace + +class TimeDomainWithObserverTest : public TimeDomainTest { + public: + MockTimeDomain* CreateMockTimeDomain() override { + observer_.reset(new MockObserver()); + return new MockTimeDomain(observer_.get()); + } + + scoped_ptr<MockObserver> observer_; +}; + +TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasImmediateWork) { + EXPECT_CALL(*observer_, OnTimeDomainHasImmediateWork()); + time_domain_->RegisterAsUpdatableTaskQueue(task_queue_.get()); +} + +TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasDelayedWork) { + EXPECT_CALL(*observer_, OnTimeDomainHasDelayedWork()); + EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)); + LazyNow lazy_now = time_domain_->CreateLazyNow(); + time_domain_->ScheduleDelayedWork( + task_queue_.get(), + time_domain_->Now() + base::TimeDelta::FromMilliseconds(10), &lazy_now); +} + } // namespace scheduler diff --git a/components/scheduler/base/virtual_time_domain.cc b/components/scheduler/base/virtual_time_domain.cc index 3251039..0ed6abb 100644 --- a/components/scheduler/base/virtual_time_domain.cc +++ b/components/scheduler/base/virtual_time_domain.cc @@ -10,8 +10,9 @@ namespace scheduler { -VirtualTimeDomain::VirtualTimeDomain(base::TimeTicks initial_time) - : now_(initial_time) {} +VirtualTimeDomain::VirtualTimeDomain(TimeDomain::Observer* observer, + base::TimeTicks initial_time) + : TimeDomain(observer), now_(initial_time) {} VirtualTimeDomain::~VirtualTimeDomain() {} @@ -44,6 +45,8 @@ void VirtualTimeDomain::AdvanceTo(base::TimeTicks now) { base::AutoLock lock(lock_); DCHECK_GE(now, now_); now_ = now; + DCHECK(task_queue_manager_delegate_); + task_queue_manager_delegate_->PostTask(FROM_HERE, do_work_closure_); } diff --git a/components/scheduler/base/virtual_time_domain.h b/components/scheduler/base/virtual_time_domain.h index ce1f99a..78239c1 100644 --- a/components/scheduler/base/virtual_time_domain.h +++ b/components/scheduler/base/virtual_time_domain.h @@ -15,7 +15,9 @@ class TaskQueueManagerDelegate; class SCHEDULER_EXPORT VirtualTimeDomain : public TimeDomain { public: - VirtualTimeDomain(base::TimeTicks initial_time); + VirtualTimeDomain(TimeDomain::Observer* observer, + base::TimeTicks initial_time); + ~VirtualTimeDomain() override; // TimeDomain implementation: LazyNow CreateLazyNow() override; @@ -41,8 +43,6 @@ class SCHEDULER_EXPORT VirtualTimeDomain : public TimeDomain { TaskQueueManagerDelegate* task_queue_manager_delegate_; // NOT OWNED base::Closure do_work_closure_; - ~VirtualTimeDomain() override; - DISALLOW_COPY_AND_ASSIGN(VirtualTimeDomain); }; diff --git a/components/scheduler/child/idle_helper.cc b/components/scheduler/child/idle_helper.cc index f007baec..b0fb06a 100644 --- a/components/scheduler/child/idle_helper.cc +++ b/components/scheduler/child/idle_helper.cc @@ -93,7 +93,7 @@ IdleHelper::IdlePeriodState IdleHelper::ComputeNewLongIdlePeriodState( if (long_idle_period_duration >= base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) { *next_long_idle_period_delay_out = long_idle_period_duration; - if (idle_queue_->IsQueueEmpty()) { + if (!idle_queue_->HasPendingImmediateTask()) { return IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED; } else if (long_idle_period_duration == max_long_idle_period_duration) { return IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE; diff --git a/components/scheduler/child/scheduler_helper.cc b/components/scheduler/child/scheduler_helper.cc index f4081fd..793f259 100644 --- a/components/scheduler/child/scheduler_helper.cc +++ b/components/scheduler/child/scheduler_helper.cc @@ -124,16 +124,28 @@ void SchedulerHelper::SetObserver(Observer* observer) { task_queue_manager_->SetObserver(this); } -void SchedulerHelper::OnUnregisterTaskQueue( - const scoped_refptr<internal::TaskQueueImpl>& queue) { - if (observer_) - observer_->OnUnregisterTaskQueue(queue); +RealTimeDomain* SchedulerHelper::real_time_domain() const { + CheckOnValidThread(); + DCHECK(task_queue_manager_); + return task_queue_manager_->real_time_domain(); } -const scoped_refptr<RealTimeDomain>& SchedulerHelper::real_time_domain() const { +void SchedulerHelper::RegisterTimeDomain(TimeDomain* time_domain) { CheckOnValidThread(); DCHECK(task_queue_manager_); - return task_queue_manager_->real_time_domain(); + task_queue_manager_->RegisterTimeDomain(time_domain); +} + +void SchedulerHelper::UnregisterTimeDomain(TimeDomain* time_domain) { + CheckOnValidThread(); + if (task_queue_manager_) + task_queue_manager_->UnregisterTimeDomain(time_domain); +} + +void SchedulerHelper::OnUnregisterTaskQueue( + const scoped_refptr<internal::TaskQueueImpl>& queue) { + if (observer_) + observer_->OnUnregisterTaskQueue(queue); } } // namespace scheduler diff --git a/components/scheduler/child/scheduler_helper.h b/components/scheduler/child/scheduler_helper.h index 27e33aa..ee727f7 100644 --- a/components/scheduler/child/scheduler_helper.h +++ b/components/scheduler/child/scheduler_helper.h @@ -83,7 +83,9 @@ class SCHEDULER_EXPORT SchedulerHelper : public TaskQueueManager::Observer { void SetObserver(Observer* observer); // Accessor methods. - const scoped_refptr<RealTimeDomain>& real_time_domain() const; + RealTimeDomain* real_time_domain() const; + void RegisterTimeDomain(TimeDomain* time_domain); + void UnregisterTimeDomain(TimeDomain* time_domain); const scoped_refptr<SchedulerTqmDelegate>& scheduler_tqm_delegate() const; bool GetAndClearSystemIsQuiescentBit(); diff --git a/components/scheduler/child/web_scheduler_impl.cc b/components/scheduler/child/web_scheduler_impl.cc index ce5436e..cc08d38 100644 --- a/components/scheduler/child/web_scheduler_impl.cc +++ b/components/scheduler/child/web_scheduler_impl.cc @@ -87,20 +87,6 @@ blink::WebTaskRunner* WebSchedulerImpl::timerTaskRunner() { return timer_web_task_runner_.get(); } -void WebSchedulerImpl::postTimerTaskAt( - const blink::WebTraceLocation& web_location, - blink::WebTaskRunner::Task* task, - double monotonicTime) { - DCHECK(timer_task_runner_); - tracked_objects::Location location(web_location.functionName(), - web_location.fileName(), -1, nullptr); - timer_task_runner_->PostDelayedTaskAt( - location, - base::Bind(&WebTaskRunnerImpl::runTask, - base::Passed(scoped_ptr<blink::WebTaskRunner::Task>(task))), - base::TimeTicks() + base::TimeDelta::FromSecondsD(monotonicTime)); -} - blink::WebPassOwnPtr<blink::WebViewScheduler> WebSchedulerImpl::createWebViewScheduler(blink::WebView*) { NOTREACHED(); diff --git a/components/scheduler/child/web_scheduler_impl.h b/components/scheduler/child/web_scheduler_impl.h index 84933ac..724491f 100644 --- a/components/scheduler/child/web_scheduler_impl.h +++ b/components/scheduler/child/web_scheduler_impl.h @@ -52,11 +52,6 @@ class SCHEDULER_EXPORT WebSchedulerImpl : public blink::WebScheduler { void removePendingNavigation() override {} void onNavigationStarted() override {} - // TODO(alexclarke): Remove when possible. - void postTimerTaskAt(const blink::WebTraceLocation& location, - blink::WebTaskRunner::Task* task, - double monotonicTime) override; - private: static void runIdleTask(scoped_ptr<blink::WebThread::IdleTask> task, base::TimeTicks deadline); diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc index 0ee5481d..0ef81cd 100644 --- a/components/scheduler/renderer/renderer_scheduler_impl.cc +++ b/components/scheduler/renderer/renderer_scheduler_impl.cc @@ -12,6 +12,7 @@ #include "cc/output/begin_frame_args.h" #include "components/scheduler/base/task_queue_impl.h" #include "components/scheduler/base/task_queue_selector.h" +#include "components/scheduler/base/virtual_time_domain.h" #include "components/scheduler/child/scheduler_tqm_delegate.h" #include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" @@ -41,6 +42,7 @@ RendererSchedulerImpl::RendererSchedulerImpl( TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererSchedulerIdlePeriod", base::TimeDelta()), + throttling_helper_(this, "renderer.scheduler"), render_widget_scheduler_signals_(this), control_task_runner_(helper_.ControlTaskRunner()), compositor_task_runner_( @@ -176,6 +178,11 @@ scoped_refptr<TaskQueue> RendererSchedulerImpl::TimerTaskRunner() { return default_timer_task_runner_; } +scoped_refptr<TaskQueue> RendererSchedulerImpl::ControlTaskRunner() { + helper_.CheckOnValidThread(); + return helper_.ControlTaskRunner(); +} + scoped_refptr<TaskQueue> RendererSchedulerImpl::NewLoadingTaskRunner( const char* name) { helper_.CheckOnValidThread(); @@ -535,7 +542,7 @@ bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() { case UseCase::MAIN_THREAD_GESTURE: case UseCase::SYNCHRONIZED_GESTURE: - return !compositor_task_runner_->IsQueueEmpty() || + return compositor_task_runner_->HasPendingImmediateTask() || MainThreadOnly().touchstart_expected_soon; case UseCase::TOUCHSTART: @@ -1046,4 +1053,16 @@ double RendererSchedulerImpl::MonotonicallyIncreasingTimeSeconds() const { static_cast<double>(base::Time::kMicrosecondsPerSecond); } +void RendererSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) { + helper_.RegisterTimeDomain(time_domain); +} + +void RendererSchedulerImpl::UnregisterTimeDomain(TimeDomain* time_domain) { + helper_.UnregisterTimeDomain(time_domain); +} + +base::TickClock* RendererSchedulerImpl::tick_clock() const { + return helper_.scheduler_tqm_delegate().get(); +} + } // namespace scheduler diff --git a/components/scheduler/renderer/renderer_scheduler_impl.h b/components/scheduler/renderer/renderer_scheduler_impl.h index 6fb097a..d4025da 100644 --- a/components/scheduler/renderer/renderer_scheduler_impl.h +++ b/components/scheduler/renderer/renderer_scheduler_impl.h @@ -15,6 +15,7 @@ #include "components/scheduler/renderer/render_widget_signals.h" #include "components/scheduler/renderer/renderer_scheduler.h" #include "components/scheduler/renderer/task_cost_estimator.h" +#include "components/scheduler/renderer/throttling_helper.h" #include "components/scheduler/renderer/user_model.h" #include "components/scheduler/scheduler_export.h" @@ -26,6 +27,7 @@ class ConvertableToTraceFormat; namespace scheduler { class RenderWidgetSchedulingState; +class ThrottlingHelper; class SCHEDULER_EXPORT RendererSchedulerImpl : public RendererScheduler, @@ -82,6 +84,12 @@ class SCHEDULER_EXPORT RendererSchedulerImpl // TaskQueueManager::Observer implementation: void OnUnregisterTaskQueue(const scoped_refptr<TaskQueue>& queue) override; + // Returns a task runner where tasks run at the highest possible priority. + scoped_refptr<TaskQueue> ControlTaskRunner(); + + void RegisterTimeDomain(TimeDomain* time_domain); + void UnregisterTimeDomain(TimeDomain* time_domain); + // Test helpers. SchedulerHelper* GetSchedulerHelperForTesting(); TaskCostEstimator* GetLoadingTaskCostEstimatorForTesting(); @@ -89,6 +97,14 @@ class SCHEDULER_EXPORT RendererSchedulerImpl IdleTimeEstimator* GetIdleTimeEstimatorForTesting(); base::TimeTicks CurrentIdleTaskDeadlineForTesting() const; + base::TickClock* tick_clock() const; + + RealTimeDomain* real_time_domain() const { + return helper_.real_time_domain(); + } + + ThrottlingHelper* throttling_helper() { return &throttling_helper_; } + private: friend class RendererSchedulerImplTest; friend class RendererSchedulerImplForTest; @@ -233,6 +249,7 @@ class SCHEDULER_EXPORT RendererSchedulerImpl SchedulerHelper helper_; IdleHelper idle_helper_; + ThrottlingHelper throttling_helper_; RenderWidgetSignals render_widget_scheduler_signals_; const scoped_refptr<TaskQueue> control_task_runner_; diff --git a/components/scheduler/renderer/throttling_helper.cc b/components/scheduler/renderer/throttling_helper.cc new file mode 100644 index 0000000..88f9ae7 --- /dev/null +++ b/components/scheduler/renderer/throttling_helper.cc @@ -0,0 +1,106 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/scheduler/renderer/throttling_helper.h" + +#include "base/logging.h" +#include "components/scheduler/base/real_time_domain.h" +#include "components/scheduler/base/virtual_time_domain.h" +#include "components/scheduler/child/scheduler_tqm_delegate.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" +#include "components/scheduler/renderer/web_frame_scheduler_impl.h" +#include "third_party/WebKit/public/platform/WebFrameScheduler.h" + +namespace scheduler { + +ThrottlingHelper::ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler, + const char* tracing_category) + : task_runner_(renderer_scheduler->ControlTaskRunner()), + renderer_scheduler_(renderer_scheduler), + tick_clock_(renderer_scheduler->tick_clock()), + tracing_category_(tracing_category), + time_domain_(new VirtualTimeDomain(this, tick_clock_->NowTicks())), + pending_pump_throttled_tasks_(false), + weak_factory_(this) { + pump_throttled_tasks_closure_ = base::Bind( + &ThrottlingHelper::PumpThrottledTasks, weak_factory_.GetWeakPtr()); + forward_immediate_work_closure_ = + base::Bind(&ThrottlingHelper::OnTimeDomainHasImmediateWork, + weak_factory_.GetWeakPtr()); + renderer_scheduler_->RegisterTimeDomain(time_domain_.get()); +} + +ThrottlingHelper::~ThrottlingHelper() { + renderer_scheduler_->UnregisterTimeDomain(time_domain_.get()); +} + +void ThrottlingHelper::Throttle(TaskQueue* task_queue) { + throttled_queues_.insert(task_queue); + + task_queue->SetTimeDomain(time_domain_.get()); + task_queue->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); + + MaybeSchedulePumpThrottledTasksLocked(); +} + +void ThrottlingHelper::Unthrottle(TaskQueue* task_queue) { + throttled_queues_.erase(task_queue); + + task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); + task_queue->SetPumpPolicy(TaskQueue::PumpPolicy::AUTO); +} + +void ThrottlingHelper::OnTimeDomainHasImmediateWork() { + // Forward to the main thread if called from another thread. + if (!task_runner_->RunsTasksOnCurrentThread()) { + task_runner_->PostTask(FROM_HERE, forward_immediate_work_closure_); + return; + } + TRACE_EVENT0(tracing_category_, + "ThrottlingHelper::OnTimeDomainHasImmediateWork"); + MaybeSchedulePumpThrottledTasksLocked(); +} + +void ThrottlingHelper::OnTimeDomainHasDelayedWork() { + TRACE_EVENT0(tracing_category_, + "ThrottlingHelper::OnTimeDomainHasDelayedWork"); + MaybeSchedulePumpThrottledTasksLocked(); +} + +void ThrottlingHelper::PumpThrottledTasks() { + TRACE_EVENT0(tracing_category_, "ThrottlingHelper::PumpThrottledTasks"); + pending_pump_throttled_tasks_ = false; + + time_domain_->AdvanceTo(tick_clock_->NowTicks()); + bool work_to_do = false; + for (TaskQueue* task_queue : throttled_queues_) { + if (task_queue->GetQueueState() == TaskQueue::QueueState::EMPTY) + continue; + + work_to_do = true; + task_queue->PumpQueue(); + } + + if (work_to_do) + MaybeSchedulePumpThrottledTasksLocked(); +} + +/* static */ +base::TimeDelta ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks now) { + const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1); + return one_second - ((now - base::TimeTicks()) % one_second); +} + +void ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked() { + if (pending_pump_throttled_tasks_) + return; + + pending_pump_throttled_tasks_ = true; + task_runner_->PostDelayedTask( + FROM_HERE, pump_throttled_tasks_closure_, + DelayToNextRunTimeInSeconds(tick_clock_->NowTicks())); +} + +} // namespace scheduler diff --git a/components/scheduler/renderer/throttling_helper.h b/components/scheduler/renderer/throttling_helper.h new file mode 100644 index 0000000..8417d53 --- /dev/null +++ b/components/scheduler/renderer/throttling_helper.h @@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SCHEDULER_RENDERER_THROTTLING_HELPER_H_ +#define COMPONENTS_SCHEDULER_RENDERER_THROTTLING_HELPER_H_ + +#include <set> + +#include "base/macros.h" +#include "components/scheduler/base/time_domain.h" +#include "components/scheduler/scheduler_export.h" +#include "third_party/WebKit/public/platform/WebViewScheduler.h" + +namespace scheduler { + +class RendererSchedulerImpl; +class VirtualTimeDomain; +class WebFrameSchedulerImpl; + +class SCHEDULER_EXPORT ThrottlingHelper : public TimeDomain::Observer { + public: + ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler, + const char* tracing_category); + + ~ThrottlingHelper() override; + + // TimeDomain::Observer implementation: + void OnTimeDomainHasImmediateWork() override; + void OnTimeDomainHasDelayedWork() override; + + void Throttle(TaskQueue* task_queue); + void Unthrottle(TaskQueue* task_queue); + + const VirtualTimeDomain* time_domain() const { return time_domain_.get(); } + + static base::TimeDelta DelayToNextRunTimeInSeconds(base::TimeTicks now); + + private: + void PumpThrottledTasks(); + void MaybeSchedulePumpThrottledTasksLocked(); + + std::set<TaskQueue*> throttled_queues_; + base::Closure pump_throttled_tasks_closure_; + base::Closure forward_immediate_work_closure_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + RendererSchedulerImpl* renderer_scheduler_; // NOT OWNED + base::TickClock* tick_clock_; // NOT OWNED + const char* tracing_category_; // NOT OWNED + scoped_ptr<VirtualTimeDomain> time_domain_; + + bool pending_pump_throttled_tasks_; + + base::WeakPtrFactory<ThrottlingHelper> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ThrottlingHelper); +}; + +} // namespace scheduler + +#endif // COMPONENTS_SCHEDULER_RENDERER_THROTTLING_HELPER_H_ diff --git a/components/scheduler/renderer/throttling_helper_unittest.cc b/components/scheduler/renderer/throttling_helper_unittest.cc new file mode 100644 index 0000000..9526f06 --- /dev/null +++ b/components/scheduler/renderer/throttling_helper_unittest.cc @@ -0,0 +1,203 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/scheduler/renderer/throttling_helper.h" + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/simple_test_tick_clock.h" +#include "cc/test/ordered_simple_task_runner.h" +#include "components/scheduler/base/test_time_source.h" +#include "components/scheduler/child/scheduler_tqm_delegate_for_test.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" +#include "components/scheduler/renderer/web_frame_scheduler_impl.h" +#include "components/scheduler/renderer/web_view_scheduler_impl.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::ElementsAre; + +namespace scheduler { + +class ThrottlingHelperTest : public testing::Test { + public: + ThrottlingHelperTest() {} + ~ThrottlingHelperTest() override {} + + void SetUp() override { + clock_.reset(new base::SimpleTestTickClock()); + clock_->Advance(base::TimeDelta::FromMicroseconds(5000)); + mock_task_runner_ = + make_scoped_refptr(new cc::OrderedSimpleTaskRunner(clock_.get(), true)); + delegate_ = SchedulerTqmDelegateForTest::Create( + mock_task_runner_, make_scoped_ptr(new TestTimeSource(clock_.get()))); + scheduler_.reset(new RendererSchedulerImpl(delegate_)); + throttling_helper_ = scheduler_->throttling_helper(); + timer_queue_ = scheduler_->NewTimerTaskRunner("test_queue"); + } + + void TearDown() override { + scheduler_->Shutdown(); + scheduler_.reset(); + } + + protected: + scoped_ptr<base::SimpleTestTickClock> clock_; + scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_; + scoped_refptr<SchedulerTqmDelegate> delegate_; + scoped_ptr<RendererSchedulerImpl> scheduler_; + scoped_refptr<TaskQueue> timer_queue_; + ThrottlingHelper* throttling_helper_; // NOT OWNED + + DISALLOW_COPY_AND_ASSIGN(ThrottlingHelperTest); +}; + +TEST_F(ThrottlingHelperTest, DelayToNextRunTimeInSeconds) { + EXPECT_EQ(base::TimeDelta::FromSecondsD(1.0), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(0.0))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.9), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(0.1))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.8), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(0.2))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.5), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(0.5))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.2), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(0.8))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.1), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(0.9))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(1.0), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.9), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(1.1))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(1.0), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(8.0))); + + EXPECT_EQ(base::TimeDelta::FromSecondsD(0.9), + ThrottlingHelper::DelayToNextRunTimeInSeconds( + base::TimeTicks() + base::TimeDelta::FromSecondsD(8.1))); +} + +namespace { +void TestTask(std::vector<base::TimeTicks>* run_times, + base::SimpleTestTickClock* clock) { + run_times->push_back(clock->NowTicks()); +} +} // namespace + +TEST_F(ThrottlingHelperTest, TimerAlignment) { + std::vector<base::TimeTicks> run_times; + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(200.0)); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(800.0)); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(1200.0)); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(8300.0)); + + throttling_helper_->Throttle(timer_queue_.get()); + + mock_task_runner_->RunUntilIdle(); + + // Times are aligned to a multipple of 1000 milliseconds. + EXPECT_THAT( + run_times, + ElementsAre( + base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0), + base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0), + base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000.0), + base::TimeTicks() + base::TimeDelta::FromMilliseconds(9000.0))); +} + +TEST_F(ThrottlingHelperTest, TimerAlignment_Unthrottled) { + std::vector<base::TimeTicks> run_times; + base::TimeTicks start_time = clock_->NowTicks(); + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(200.0)); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(800.0)); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(1200.0)); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(8300.0)); + + throttling_helper_->Throttle(timer_queue_.get()); + throttling_helper_->Unthrottle(timer_queue_.get()); + + mock_task_runner_->RunUntilIdle(); + + // Times are not aligned. + EXPECT_THAT( + run_times, + ElementsAre(start_time + base::TimeDelta::FromMilliseconds(200.0), + start_time + base::TimeDelta::FromMilliseconds(800.0), + start_time + base::TimeDelta::FromMilliseconds(1200.0), + start_time + base::TimeDelta::FromMilliseconds(8300.0))); +} + +TEST_F(ThrottlingHelperTest, WakeUpForNonDelayedTask) { + std::vector<base::TimeTicks> run_times; + + // Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick. + throttling_helper_->Throttle(timer_queue_.get()); + + // Posting a task should trigger the pump. + timer_queue_->PostTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get())); + + mock_task_runner_->RunUntilIdle(); + EXPECT_THAT(run_times, + ElementsAre(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1000.0))); +} + +TEST_F(ThrottlingHelperTest, WakeUpForDelayedTask) { + std::vector<base::TimeTicks> run_times; + + // Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick. + throttling_helper_->Throttle(timer_queue_.get()); + + // Posting a task should trigger the pump. + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(1200.0)); + + mock_task_runner_->RunUntilIdle(); + EXPECT_THAT(run_times, + ElementsAre(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(2000.0))); +} + +} // namespace scheduler diff --git a/components/scheduler/renderer/web_frame_scheduler_impl.cc b/components/scheduler/renderer/web_frame_scheduler_impl.cc index 15e4a7d..b8c1c1a 100644 --- a/components/scheduler/renderer/web_frame_scheduler_impl.cc +++ b/components/scheduler/renderer/web_frame_scheduler_impl.cc @@ -4,6 +4,8 @@ #include "components/scheduler/renderer/web_frame_scheduler_impl.h" +#include "components/scheduler/base/real_time_domain.h" +#include "components/scheduler/base/virtual_time_domain.h" #include "components/scheduler/child/web_task_runner_impl.h" #include "components/scheduler/renderer/renderer_scheduler_impl.h" #include "components/scheduler/renderer/web_view_scheduler_impl.h" @@ -16,14 +18,18 @@ WebFrameSchedulerImpl::WebFrameSchedulerImpl( WebViewSchedulerImpl* parent_web_view_scheduler) : renderer_scheduler_(renderer_scheduler), parent_web_view_scheduler_(parent_web_view_scheduler), - visible_(true) {} + visible_(true), + page_in_background_(false) {} WebFrameSchedulerImpl::~WebFrameSchedulerImpl() { if (loading_task_queue_.get()) loading_task_queue_->UnregisterTaskQueue(); - if (timer_task_queue_.get()) + if (timer_task_queue_.get()) { + renderer_scheduler_->throttling_helper()->Unthrottle( + timer_task_queue_.get()); timer_task_queue_->UnregisterTaskQueue(); + } if (parent_web_view_scheduler_) parent_web_view_scheduler_->Unregister(this); @@ -51,6 +57,10 @@ blink::WebTaskRunner* WebFrameSchedulerImpl::timerTaskRunner() { if (!timer_web_task_runner_) { timer_task_queue_ = renderer_scheduler_->NewTimerTaskRunner("frame_timer_tq"); + if (page_in_background_) { + renderer_scheduler_->throttling_helper()->Throttle( + timer_task_queue_.get()); + } timer_web_task_runner_.reset(new WebTaskRunnerImpl(timer_task_queue_)); } return timer_web_task_runner_.get(); @@ -63,4 +73,21 @@ void WebFrameSchedulerImpl::setFrameOrigin( // TODO(skyostil): Associate the task queues with this origin. } +void WebFrameSchedulerImpl::SetPageInBackground(bool page_in_background) { + if (page_in_background_ == page_in_background) + return; + + page_in_background_ = page_in_background; + + if (!timer_web_task_runner_) + return; + + if (page_in_background_) { + renderer_scheduler_->throttling_helper()->Throttle(timer_task_queue_.get()); + } else { + renderer_scheduler_->throttling_helper()->Unthrottle( + timer_task_queue_.get()); + } +} + } // namespace scheduler diff --git a/components/scheduler/renderer/web_frame_scheduler_impl.h b/components/scheduler/renderer/web_frame_scheduler_impl.h index eb14bc0..96a9e19 100644 --- a/components/scheduler/renderer/web_frame_scheduler_impl.h +++ b/components/scheduler/renderer/web_frame_scheduler_impl.h @@ -36,10 +36,13 @@ class SCHEDULER_EXPORT WebFrameSchedulerImpl : public blink::WebFrameScheduler { blink::WebTaskRunner* timerTaskRunner() override; void setFrameOrigin(const blink::WebSecurityOrigin* origin) override; + void SetPageInBackground(bool page_in_background); + private: friend class WebViewSchedulerImpl; void DetachFromWebViewScheduler(); + void ApplyPolicyToTimerQueue(); scoped_refptr<TaskQueue> loading_task_queue_; scoped_refptr<TaskQueue> timer_task_queue_; @@ -49,6 +52,7 @@ class SCHEDULER_EXPORT WebFrameSchedulerImpl : public blink::WebFrameScheduler { WebViewSchedulerImpl* parent_web_view_scheduler_; // NOT OWNED blink::WebSecurityOrigin origin_; bool visible_; + bool page_in_background_; DISALLOW_COPY_AND_ASSIGN(WebFrameSchedulerImpl); }; diff --git a/components/scheduler/renderer/web_view_scheduler_impl.cc b/components/scheduler/renderer/web_view_scheduler_impl.cc index 28bc216..658fdbb 100644 --- a/components/scheduler/renderer/web_view_scheduler_impl.cc +++ b/components/scheduler/renderer/web_view_scheduler_impl.cc @@ -5,6 +5,9 @@ #include "components/scheduler/renderer/web_view_scheduler_impl.h" #include "base/logging.h" +#include "components/scheduler/base/virtual_time_domain.h" +#include "components/scheduler/child/scheduler_tqm_delegate.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" #include "components/scheduler/renderer/web_frame_scheduler_impl.h" #include "third_party/WebKit/public/platform/WebFrameScheduler.h" @@ -15,7 +18,7 @@ WebViewSchedulerImpl::WebViewSchedulerImpl( RendererSchedulerImpl* renderer_scheduler) : web_view_(web_view), renderer_scheduler_(renderer_scheduler), - background_(false) {} + page_in_background_(false) {} WebViewSchedulerImpl::~WebViewSchedulerImpl() { // TODO(alexclarke): Find out why we can't rely on the web view outliving the @@ -25,17 +28,29 @@ WebViewSchedulerImpl::~WebViewSchedulerImpl() { } } -void WebViewSchedulerImpl::setPageInBackground(bool background) { - background_ = background; - // TODO(alexclarke): Do something with this flag. +void WebViewSchedulerImpl::setPageInBackground(bool page_in_background) { + if (page_in_background_ == page_in_background) + return; + + page_in_background_ = page_in_background; + + for (WebFrameSchedulerImpl* frame_scheduler : frame_schedulers_) { + frame_scheduler->SetPageInBackground(page_in_background_); + } } -blink::WebPassOwnPtr<blink::WebFrameScheduler> -WebViewSchedulerImpl::createFrameScheduler() { +scoped_ptr<WebFrameSchedulerImpl> +WebViewSchedulerImpl::createWebFrameSchedulerImpl() { scoped_ptr<WebFrameSchedulerImpl> frame_scheduler( new WebFrameSchedulerImpl(renderer_scheduler_, this)); + frame_scheduler->SetPageInBackground(page_in_background_); frame_schedulers_.insert(frame_scheduler.get()); - return blink::adoptWebPtr(frame_scheduler.release()); + return frame_scheduler.Pass(); +} + +blink::WebPassOwnPtr<blink::WebFrameScheduler> +WebViewSchedulerImpl::createFrameScheduler() { + return blink::adoptWebPtr(createWebFrameSchedulerImpl().release()); } void WebViewSchedulerImpl::Unregister(WebFrameSchedulerImpl* frame_scheduler) { diff --git a/components/scheduler/renderer/web_view_scheduler_impl.h b/components/scheduler/renderer/web_view_scheduler_impl.h index 647d6d9..a5dd7d7 100644 --- a/components/scheduler/renderer/web_view_scheduler_impl.h +++ b/components/scheduler/renderer/web_view_scheduler_impl.h @@ -8,6 +8,7 @@ #include <set> #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "components/scheduler/scheduler_export.h" #include "third_party/WebKit/public/platform/WebViewScheduler.h" @@ -32,12 +33,14 @@ class SCHEDULER_EXPORT WebViewSchedulerImpl : public blink::WebViewScheduler { ~WebViewSchedulerImpl() override; // blink::WebViewScheduler implementation: - void setPageInBackground(bool background) override; + void setPageInBackground(bool page_in_background) override; blink::WebPassOwnPtr<blink::WebFrameScheduler> createFrameScheduler() override; blink::WebView* web_view() const { return web_view_; } + scoped_ptr<WebFrameSchedulerImpl> createWebFrameSchedulerImpl(); + private: friend class WebFrameSchedulerImpl; @@ -46,7 +49,7 @@ class SCHEDULER_EXPORT WebViewSchedulerImpl : public blink::WebViewScheduler { std::set<WebFrameSchedulerImpl*> frame_schedulers_; blink::WebView* web_view_; RendererSchedulerImpl* renderer_scheduler_; - bool background_; + bool page_in_background_; DISALLOW_COPY_AND_ASSIGN(WebViewSchedulerImpl); }; diff --git a/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc b/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc index 401b39a..750811c 100644 --- a/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc +++ b/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc @@ -13,6 +13,8 @@ #include "components/scheduler/renderer/renderer_scheduler_impl.h" #include "components/scheduler/renderer/web_frame_scheduler_impl.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebTaskRunner.h" +#include "third_party/WebKit/public/platform/WebTraceLocation.h" namespace scheduler { @@ -24,22 +26,29 @@ class WebViewSchedulerImplTest : public testing::Test { void SetUp() override { clock_.reset(new base::SimpleTestTickClock()); clock_->Advance(base::TimeDelta::FromMicroseconds(5000)); - mock_task_runner_ = make_scoped_refptr( - new cc::OrderedSimpleTaskRunner(clock_.get(), false)); - main_task_runner_ = SchedulerTqmDelegateForTest::Create( + mock_task_runner_ = + make_scoped_refptr(new cc::OrderedSimpleTaskRunner(clock_.get(), true)); + delagate_ = SchedulerTqmDelegateForTest::Create( mock_task_runner_, make_scoped_ptr(new TestTimeSource(clock_.get()))); - scheduler_.reset(new RendererSchedulerImpl(main_task_runner_)); + scheduler_.reset(new RendererSchedulerImpl(delagate_)); web_view_scheduler_.reset( new WebViewSchedulerImpl(nullptr, scheduler_.get())); + web_frame_scheduler_ = web_view_scheduler_->createWebFrameSchedulerImpl(); } - void TearDown() override { scheduler_->Shutdown(); } + void TearDown() override { + web_frame_scheduler_.reset(); + web_view_scheduler_.reset(); + scheduler_->Shutdown(); + scheduler_.reset(); + } scoped_ptr<base::SimpleTestTickClock> clock_; scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_; - scoped_refptr<SchedulerTqmDelegate> main_task_runner_; + scoped_refptr<SchedulerTqmDelegate> delagate_; scoped_ptr<RendererSchedulerImpl> scheduler_; scoped_ptr<WebViewSchedulerImpl> web_view_scheduler_; + scoped_ptr<WebFrameSchedulerImpl> web_frame_scheduler_; }; TEST_F(WebViewSchedulerImplTest, TestDestructionOfFrameSchedulersBefore) { @@ -57,4 +66,88 @@ TEST_F(WebViewSchedulerImplTest, TestDestructionOfFrameSchedulersAfter) { web_view_scheduler_.reset(); } +namespace { +class RepeatingTask : public blink::WebTaskRunner::Task { + public: + RepeatingTask(blink::WebTaskRunner* web_task_runner, int* run_count) + : web_task_runner_(web_task_runner), run_count_(run_count) {} + + ~RepeatingTask() override {} + + void run() override { + (*run_count_)++; + web_task_runner_->postDelayedTask( + BLINK_FROM_HERE, new RepeatingTask(web_task_runner_, run_count_), 1.0); + } + + private: + blink::WebTaskRunner* web_task_runner_; // NOT OWNED + int* run_count_; // NOT OWNED +}; +} // namespace + +TEST_F(WebViewSchedulerImplTest, RepeatingTimer_PageInForeground) { + web_view_scheduler_->setPageInBackground(false); + + int run_count = 0; + web_frame_scheduler_->timerTaskRunner()->postDelayedTask( + BLINK_FROM_HERE, + new RepeatingTask(web_frame_scheduler_->timerTaskRunner(), &run_count), + 1.0); + + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(1000, run_count); +} + +TEST_F(WebViewSchedulerImplTest, RepeatingTimer_PageInBackground) { + web_view_scheduler_->setPageInBackground(true); + + int run_count = 0; + web_frame_scheduler_->timerTaskRunner()->postDelayedTask( + BLINK_FROM_HERE, + new RepeatingTask(web_frame_scheduler_->timerTaskRunner(), &run_count), + 1.0); + + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(1, run_count); +} + +TEST_F(WebViewSchedulerImplTest, RepeatingLoadingTask_PageInBackground) { + web_view_scheduler_->setPageInBackground(true); + + int run_count = 0; + web_frame_scheduler_->loadingTaskRunner()->postDelayedTask( + BLINK_FROM_HERE, + new RepeatingTask(web_frame_scheduler_->loadingTaskRunner(), &run_count), + 1.0); + + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(1000, run_count); // Loading tasks should not be throttled +} + +TEST_F(WebViewSchedulerImplTest, RepeatingTimers_OneBackgroundOneForeground) { + scoped_ptr<WebViewSchedulerImpl> web_view_scheduler2( + new WebViewSchedulerImpl(nullptr, scheduler_.get())); + scoped_ptr<WebFrameSchedulerImpl> web_frame_scheduler2 = + web_view_scheduler2->createWebFrameSchedulerImpl(); + + web_view_scheduler_->setPageInBackground(false); + web_view_scheduler2->setPageInBackground(true); + + int run_count1 = 0; + int run_count2 = 0; + web_frame_scheduler_->timerTaskRunner()->postDelayedTask( + BLINK_FROM_HERE, + new RepeatingTask(web_frame_scheduler_->timerTaskRunner(), &run_count1), + 1.0); + web_frame_scheduler2->timerTaskRunner()->postDelayedTask( + BLINK_FROM_HERE, + new RepeatingTask(web_frame_scheduler2->timerTaskRunner(), &run_count2), + 1.0); + + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(1000, run_count1); + EXPECT_EQ(1, run_count2); +} + } // namespace scheduler diff --git a/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc index 52f4ee8..c19e75e 100644 --- a/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc +++ b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc @@ -35,27 +35,30 @@ class MockTaskObserver : public blink::WebThread::TaskObserver { class WebThreadImplForRendererSchedulerTest : public testing::Test { public: - WebThreadImplForRendererSchedulerTest() - : clock_(new base::SimpleTestTickClock()), - scheduler_(SchedulerTqmDelegateImpl::Create( - &message_loop_, - make_scoped_ptr(new TestTimeSource(clock_.get())))), - default_task_runner_(scheduler_.DefaultTaskRunner()), - thread_(scheduler_.CreateMainThread()) {} + WebThreadImplForRendererSchedulerTest() {} + + void SetUp() override { + clock_.reset(new base::SimpleTestTickClock()); + clock_->Advance(base::TimeDelta::FromMicroseconds(5000)); + scheduler_.reset(new RendererSchedulerImpl(SchedulerTqmDelegateImpl::Create( + &message_loop_, make_scoped_ptr(new TestTimeSource(clock_.get()))))); + default_task_runner_ = scheduler_->DefaultTaskRunner(); + thread_ = scheduler_->CreateMainThread(); + } ~WebThreadImplForRendererSchedulerTest() override {} void SetWorkBatchSizeForTesting(size_t work_batch_size) { - scheduler_.GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting( + scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting( work_batch_size); } - void TearDown() override { scheduler_.Shutdown(); } + void TearDown() override { scheduler_->Shutdown(); } protected: base::MessageLoop message_loop_; scoped_ptr<base::SimpleTestTickClock> clock_; - RendererSchedulerImpl scheduler_; + scoped_ptr<RendererSchedulerImpl> scheduler_; scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_; scoped_ptr<blink::WebThread> thread_; diff --git a/components/scheduler/scheduler.gypi b/components/scheduler/scheduler.gypi index dbd40ce..c6a79b7 100644 --- a/components/scheduler/scheduler.gypi +++ b/components/scheduler/scheduler.gypi @@ -72,12 +72,14 @@ 'renderer/render_widget_scheduling_state.h', 'renderer/render_widget_signals.cc', 'renderer/render_widget_signals.h', + 'renderer/task_cost_estimator.cc', + 'renderer/task_cost_estimator.h', + 'renderer/throttling_helper.cc', + 'renderer/throttling_helper.h', 'renderer/web_frame_scheduler_impl.cc', 'renderer/web_frame_scheduler_impl.h', 'renderer/web_view_scheduler_impl.cc', 'renderer/web_view_scheduler_impl.h', - 'renderer/task_cost_estimator.cc', - 'renderer/task_cost_estimator.h', 'renderer/user_model.cc', 'renderer/user_model.h', 'renderer/webthread_impl_for_renderer_scheduler.cc', diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp index 8b824c9..795e85e 100644 --- a/third_party/WebKit/Source/core/dom/Document.cpp +++ b/third_party/WebKit/Source/core/dom/Document.cpp @@ -442,7 +442,7 @@ Document::Document(const DocumentInit& initializer, DocumentClassFlags documentC , m_timeline(AnimationTimeline::create(this)) , m_templateDocumentHost(nullptr) , m_didAssociateFormControlsTimer(this, &Document::didAssociateFormControlsTimerFired) - , m_timers(Platform::current()->currentThread()->scheduler()->timerTaskRunner()->adoptClone()) + , m_timers(timerTaskRunner()->adoptClone()) , m_hasViewportUnits(false) , m_styleRecalcElementCounter(0) , m_parserSyncPolicy(AllowAsynchronousParsing) @@ -453,7 +453,6 @@ Document::Document(const DocumentInit& initializer, DocumentClassFlags documentC provideContextFeaturesToDocumentFrom(*this, *m_frame->page()); m_fetcher = m_frame->loader().documentLoader()->fetcher(); - m_timers.setTimerTaskRunner(m_frame->frameScheduler()->timerTaskRunner()->adoptClone()); FrameFetchContext::provideDocumentToContext(m_fetcher->context(), this); } else if (m_importsController) { m_fetcher = FrameFetchContext::createContextAndFetcher(nullptr); @@ -2877,14 +2876,6 @@ KURL Document::virtualCompleteURL(const String& url) const return completeURL(url); } -double Document::timerAlignmentInterval() const -{ - Page* p = page(); - if (!p) - return DOMTimer::visiblePageAlignmentInterval(); - return p->timerAlignmentInterval(); -} - DOMTimerCoordinator* Document::timers() { return &m_timers; @@ -3025,8 +3016,7 @@ void Document::didRemoveAllPendingStylesheet() void Document::didLoadAllScriptBlockingResources() { - Platform::current()->currentThread()->scheduler()->loadingTaskRunner()->postTask( - BLINK_FROM_HERE, m_executeScriptsWaitingForResourcesTask->cancelAndCreate()); + loadingTaskRunner()->postTask(BLINK_FROM_HERE, m_executeScriptsWaitingForResourcesTask->cancelAndCreate()); if (frame()) frame()->loader().client()->didRemoveAllPendingStylesheet(); @@ -5750,12 +5740,22 @@ WebTaskRunner* Document::loadingTaskRunner() const { if (frame()) return frame()->frameScheduler()->loadingTaskRunner(); + if (m_importsController) + return m_importsController->master()->loadingTaskRunner(); + if (m_contextDocument) + return m_contextDocument->loadingTaskRunner(); return Platform::current()->currentThread()->scheduler()->loadingTaskRunner(); } WebTaskRunner* Document::timerTaskRunner() const { - return m_timers.timerTaskRunner(); + if (frame()) + return m_frame->frameScheduler()->timerTaskRunner(); + if (m_importsController) + return m_importsController->master()->timerTaskRunner(); + if (m_contextDocument) + return m_contextDocument->timerTaskRunner(); + return Platform::current()->currentThread()->scheduler()->timerTaskRunner(); } DEFINE_TRACE(Document) diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h index 22555f4..e4af2d6 100644 --- a/third_party/WebKit/Source/core/dom/Document.h +++ b/third_party/WebKit/Source/core/dom/Document.h @@ -1116,8 +1116,6 @@ private: void reportBlockedScriptExecutionToInspector(const String& directiveText) final; - double timerAlignmentInterval() const final; - void updateTitle(const String&); void updateFocusAppearanceTimerFired(Timer<Document>*); void updateBaseURL(); diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.h b/third_party/WebKit/Source/core/dom/ExecutionContext.h index ee4af7d..49b7e8f 100644 --- a/third_party/WebKit/Source/core/dom/ExecutionContext.h +++ b/third_party/WebKit/Source/core/dom/ExecutionContext.h @@ -89,7 +89,6 @@ public: virtual LocalDOMWindow* executingWindow() { return 0; } virtual String userAgent() const = 0; virtual void postTask(const WebTraceLocation&, PassOwnPtr<ExecutionContextTask>) = 0; // Executes the task on context's thread asynchronously. - virtual double timerAlignmentInterval() const = 0; // Gets the DOMTimerCoordinator which maintains the "active timer // list" of tasks created by setTimeout and setInterval. The diff --git a/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp b/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp index 09e5579..db7a582 100644 --- a/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp +++ b/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp @@ -137,7 +137,6 @@ public: void postIdleTask(const WebTraceLocation&, WebThread::IdleTask*) override { } void postNonNestableIdleTask(const WebTraceLocation&, WebThread::IdleTask*) override { } void postIdleTaskAfterWakeup(const WebTraceLocation&, WebThread::IdleTask*) override { } - void postTimerTaskAt(const WebTraceLocation&, WebTaskRunner::Task*, double monotonicTime) override { } WebPassOwnPtr<WebViewScheduler> createWebViewScheduler(blink::WebView*) override { return nullptr; } void suspendTimerQueue() override { } void resumeTimerQueue() override { } diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.cpp b/third_party/WebKit/Source/core/frame/DOMTimer.cpp index b820cc6..cdfb3d8 100644 --- a/third_party/WebKit/Source/core/frame/DOMTimer.cpp +++ b/third_party/WebKit/Source/core/frame/DOMTimer.cpp @@ -52,19 +52,6 @@ static inline bool shouldForwardUserGesture(int interval, int nestingLevel) && nestingLevel == 1; // Gestures should not be forwarded to nested timers. } -double DOMTimer::hiddenPageAlignmentInterval() -{ - // Timers on hidden pages are aligned so that they fire once per - // second at most. - return 1.0; -} - -double DOMTimer::visiblePageAlignmentInterval() -{ - // Alignment does not apply to timers on visible pages. - return 0; -} - int DOMTimer::install(ExecutionContext* context, PassOwnPtrWillBeRawPtr<ScheduledAction> action, int timeout, bool singleShot) { int timeoutID = context->timers()->installNewTimeout(context, action, timeout, singleShot); @@ -164,42 +151,6 @@ void DOMTimer::stop() m_action.clear(); } -double DOMTimer::alignedFireTime(double fireTime) const -{ - double alignmentInterval = executionContext()->timerAlignmentInterval(); - if (alignmentInterval) { - double currentTime = monotonicallyIncreasingTime(); - if (fireTime <= currentTime) - return fireTime; - - // When a repeating timer is scheduled for exactly the - // background page alignment interval, because it's impossible - // for the timer to be rescheduled instantaneously, it misses - // every other fire time. Avoid this by looking at the next - // fire time rounded both down and up. - - double alignedTimeRoundedDown = floor(fireTime / alignmentInterval) * alignmentInterval; - double alignedTimeRoundedUp = ceil(fireTime / alignmentInterval) * alignmentInterval; - - // If the version rounded down is in the past, discard it - // immediately. - - if (alignedTimeRoundedDown <= currentTime) - return alignedTimeRoundedUp; - - // Only use the rounded-down time if it's within a certain - // tolerance of the fire time. This avoids speeding up timers - // on background pages in the common case. - - if (fireTime - alignedTimeRoundedDown < minimumInterval) - return alignedTimeRoundedDown; - - return alignedTimeRoundedUp; - } - - return fireTime; -} - WebTaskRunner* DOMTimer::timerTaskRunner() { return executionContext()->timers()->timerTaskRunner(); diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.h b/third_party/WebKit/Source/core/frame/DOMTimer.h index 4912ace9..59236c8 100644 --- a/third_party/WebKit/Source/core/frame/DOMTimer.h +++ b/third_party/WebKit/Source/core/frame/DOMTimer.h @@ -52,10 +52,6 @@ public: // ActiveDOMObject void stop() override; - // The following are essentially constants. All intervals are in seconds. - static double hiddenPageAlignmentInterval(); - static double visiblePageAlignmentInterval(); - // Eager finalization is needed to promptly stop this Timer object. // Otherwise timer events might fire at an object that's slated for destruction // (when lazily swept), but some of its members (m_action) may already have @@ -76,9 +72,6 @@ private: DOMTimer(ExecutionContext*, PassOwnPtrWillBeRawPtr<ScheduledAction>, int interval, bool singleShot, int timeoutID); void fired() override; - // Retuns timer fire time rounded to the next multiple of timer alignment interval. - double alignedFireTime(double) const override; - WebTaskRunner* timerTaskRunner() override; int m_timeoutID; diff --git a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp index 240f8cb..4401db9 100644 --- a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp +++ b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp @@ -42,20 +42,6 @@ void DOMTimerCoordinator::removeTimeoutByID(int timeoutID) m_timers.remove(timeoutID); } -void DOMTimerCoordinator::didChangeTimerAlignmentInterval() -{ - // Reschedule timers in increasing order of desired run time to maintain their relative order. - // TODO(skyostil): Move timer alignment into the scheduler. - WillBeHeapVector<RawPtrWillBeMember<DOMTimer>> timers; - timers.reserveCapacity(m_timers.size()); - for (TimeoutMap::iterator iter = m_timers.begin(); iter != m_timers.end(); ++iter) - timers.append(iter->value.get()); - std::sort(timers.begin(), timers.end(), TimerBase::Comparator()); - double now = monotonicallyIncreasingTime(); - for (DOMTimer* timer : timers) - timer->didChangeAlignmentInterval(now); -} - DEFINE_TRACE(DOMTimerCoordinator) { #if ENABLE(OILPAN) diff --git a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h index 69c8d22..2c7f0cd 100644 --- a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h +++ b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h @@ -34,11 +34,6 @@ public: // destroy the timer. void removeTimeoutByID(int id); - // Notifies registered timers that - // ExecutionContext::timerAlignmentInterval has changed so that - // timers can adjust their schedule to the new alignment interval. - void didChangeTimerAlignmentInterval(); - // Timers created during the execution of other timers, and // repeating timers, are throttled. Timer nesting level tracks the // number of linked timers or repetitions of a timer. See diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp index 34fa2a0..4d289e4 100644 --- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp +++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp @@ -149,10 +149,7 @@ public: SuspendableTimer::trace(visitor); } - WebTaskRunner* timerTaskRunner() override - { - return m_window->document()->timerTaskRunner(); - } + // TODO(alexclarke): Override timerTaskRunner() to pass in a document specific default task runner. private: void fired() override diff --git a/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp b/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp index 8a9d048..b2d690a 100644 --- a/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp +++ b/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp @@ -66,7 +66,7 @@ void SuspendableTimer::suspend() m_suspended = true; #endif if (isActive()) { - m_nextFireInterval = nextUnalignedFireInterval(); + m_nextFireInterval = nextFireInterval(); ASSERT(m_nextFireInterval >= 0.0); m_repeatInterval = repeatInterval(); TimerBase::stop(); diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp index dac637d..3ec85f1 100644 --- a/third_party/WebKit/Source/core/page/Page.cpp +++ b/third_party/WebKit/Source/core/page/Page.cpp @@ -117,7 +117,6 @@ Page::Page(PageClients& pageClients) , m_tabKeyCyclesThroughElements(true) , m_defersLoading(false) , m_deviceScaleFactor(1) - , m_timerAlignmentInterval(DOMTimer::visiblePageAlignmentInterval()) , m_visibilityState(PageVisibilityStateVisible) , m_isCursorVisible(true) #if ENABLE(ASSERT) @@ -356,41 +355,15 @@ void Page::visitedStateChanged(LinkHash linkHash) } } -void Page::setTimerAlignmentInterval(double interval) -{ - if (interval == m_timerAlignmentInterval) - return; - - m_timerAlignmentInterval = interval; - for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNextWithWrap(false)) { - if (!frame->isLocalFrame()) - continue; - - if (Document* document = toLocalFrame(frame)->document()) { - if (DOMTimerCoordinator* timers = document->timers()) { - timers->didChangeTimerAlignmentInterval(); - } - } - } -} - -double Page::timerAlignmentInterval() const -{ - return m_timerAlignmentInterval; -} - void Page::setVisibilityState(PageVisibilityState visibilityState, bool isInitialState) { if (m_visibilityState == visibilityState) return; m_visibilityState = visibilityState; - // TODO(alexclarke): Move throttling of timers to chromium. if (visibilityState == PageVisibilityStateVisible) { - setTimerAlignmentInterval(DOMTimer::visiblePageAlignmentInterval()); memoryPurgeController().pageBecameActive(); } else { - setTimerAlignmentInterval(DOMTimer::hiddenPageAlignmentInterval()); if (!isInitialState) memoryPurgeController().pageBecameInactive(); } diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h index 15cfbc5..1ce3024 100644 --- a/third_party/WebKit/Source/core/page/Page.h +++ b/third_party/WebKit/Source/core/page/Page.h @@ -185,8 +185,6 @@ public: bool isPainting() const { return m_isPainting; } #endif - double timerAlignmentInterval() const; - class CORE_EXPORT MultisamplingChangedObserver : public WillBeGarbageCollectedMixin { public: virtual void multisamplingChanged(bool) = 0; @@ -213,8 +211,6 @@ public: private: void initGroup(); - void setTimerAlignmentInterval(double); - void setNeedsLayoutInAllFrames(); // SettingsDelegate overrides. @@ -261,8 +257,6 @@ private: float m_deviceScaleFactor; - double m_timerAlignmentInterval; - PageVisibilityState m_visibilityState; bool m_isCursorVisible; diff --git a/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp b/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp index e3e79bc..46ab161 100644 --- a/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp +++ b/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp @@ -34,11 +34,6 @@ void NullExecutionContext::postTask(const WebTraceLocation&, PassOwnPtr<Executio { } -double NullExecutionContext::timerAlignmentInterval() const -{ - return DOMTimer::visiblePageAlignmentInterval(); -} - bool NullExecutionContext::isSecureContext(String& errorMessage, const SecureContextCheck privilegeContextCheck) const { return true; diff --git a/third_party/WebKit/Source/core/testing/NullExecutionContext.h b/third_party/WebKit/Source/core/testing/NullExecutionContext.h index 9cb49be..4838afd 100644 --- a/third_party/WebKit/Source/core/testing/NullExecutionContext.h +++ b/third_party/WebKit/Source/core/testing/NullExecutionContext.h @@ -37,8 +37,6 @@ public: SecurityContext& securityContext() override { return *this; } DOMTimerCoordinator* timers() override { return nullptr; } - double timerAlignmentInterval() const; - void addConsoleMessage(PassRefPtrWillBeRawPtr<ConsoleMessage>) override { } void logExceptionToConsole(const String& errorMessage, int scriptId, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>) override { } diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp index adfe55e..d5776ef 100644 --- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp @@ -148,11 +148,6 @@ void WorkerGlobalScope::disableEval(const String& errorMessage) m_script->disableEval(errorMessage); } -double WorkerGlobalScope::timerAlignmentInterval() const -{ - return DOMTimer::visiblePageAlignmentInterval(); -} - DOMTimerCoordinator* WorkerGlobalScope::timers() { return &m_timers; diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h index 0a0c4cb..b03f823 100644 --- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h @@ -120,7 +120,6 @@ public: bool isContextThread() const final; bool isJSExecutionForbidden() const final; - double timerAlignmentInterval() const final; DOMTimerCoordinator* timers() final; WorkerInspectorController* workerInspectorController() { return m_workerInspectorController.get(); } diff --git a/third_party/WebKit/Source/platform/Timer.cpp b/third_party/WebKit/Source/platform/Timer.cpp index ad619a3..e27520d 100644 --- a/third_party/WebKit/Source/platform/Timer.cpp +++ b/third_party/WebKit/Source/platform/Timer.cpp @@ -43,7 +43,6 @@ namespace blink { TimerBase::TimerBase() : m_nextFireTime(0) - , m_unalignedNextFireTime(0) , m_repeatInterval(0) , m_cancellableTimerTask(nullptr) , m_webScheduler(Platform::current()->currentThread()->scheduler()) @@ -96,23 +95,16 @@ void TimerBase::setNextFireTime(double now, double delay) { ASSERT(m_thread == currentThread()); - m_unalignedNextFireTime = now + delay; + double newTime = now + delay; - double newTime = alignedFireTime(m_unalignedNextFireTime); if (m_nextFireTime != newTime) { m_nextFireTime = newTime; if (m_cancellableTimerTask) m_cancellableTimerTask->cancel(); m_cancellableTimerTask = new CancellableTimerTask(this); - if (newTime != m_unalignedNextFireTime) { - // If the timer is being aligned, use postTimerTaskAt() to schedule it - // so that the relative order of aligned timers is preserved. - // TODO(skyostil): Move timer alignment into the scheduler. - m_webScheduler->postTimerTaskAt(m_location, m_cancellableTimerTask, m_nextFireTime); - } else { - double delayMs = 1000.0 * (newTime - now); - m_webScheduler->timerTaskRunner()->postDelayedTask(m_location, m_cancellableTimerTask, delayMs); - } + + double delayMs = 1000.0 * (newTime - now); + timerTaskRunner()->postDelayedTask(m_location, m_cancellableTimerTask, delayMs); } } @@ -126,34 +118,24 @@ void TimerBase::runInternal() ASSERT_WITH_MESSAGE(m_thread == currentThread(), "Timer posted by %s %s was run on a different thread", m_location.functionName(), m_location.fileName()); TRACE_EVENT_SET_SAMPLING_STATE("blink", "BlinkInternal"); - m_nextFireTime = 0; if (m_repeatInterval) { double now = monotonicallyIncreasingTime(); // 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. - double intervalToNextFireTime = m_repeatInterval - fmod(now - m_unalignedNextFireTime, m_repeatInterval); + double intervalToNextFireTime = m_repeatInterval - fmod(now - m_nextFireTime, m_repeatInterval); setNextFireTime(monotonicallyIncreasingTime(), intervalToNextFireTime); + } else { + m_nextFireTime = 0; } fired(); TRACE_EVENT_SET_SAMPLING_STATE("blink", "Sleeping"); } -void TimerBase::didChangeAlignmentInterval(double now) -{ - setNextFireTime(now, m_unalignedNextFireTime - now); -} - -double TimerBase::nextUnalignedFireInterval() const -{ - ASSERT(isActive()); - return std::max(m_unalignedNextFireTime - monotonicallyIncreasingTime(), 0.0); -} - bool TimerBase::Comparator::operator()(const TimerBase* a, const TimerBase* b) const { - return a->m_unalignedNextFireTime < b->m_unalignedNextFireTime; + return a->m_nextFireTime < b->m_nextFireTime; } } // namespace blink diff --git a/third_party/WebKit/Source/platform/Timer.h b/third_party/WebKit/Source/platform/Timer.h index f2c4aa3..d6616a8 100644 --- a/third_party/WebKit/Source/platform/Timer.h +++ b/third_party/WebKit/Source/platform/Timer.h @@ -61,7 +61,6 @@ public: const WebTraceLocation& location() const { return m_location; } double nextFireInterval() const; - double nextUnalignedFireInterval() const; double repeatInterval() const { return m_repeatInterval; } void augmentRepeatInterval(double delta) { @@ -70,8 +69,6 @@ public: m_repeatInterval += delta; } - void didChangeAlignmentInterval(double now); - struct PLATFORM_EXPORT Comparator { bool operator()(const TimerBase* a, const TimerBase* b) const; }; @@ -121,7 +118,6 @@ private: }; double m_nextFireTime; // 0 if inactive - double m_unalignedNextFireTime; // m_nextFireTime not considering alignment interval double m_repeatInterval; // 0 if not repeating WebTraceLocation m_location; CancellableTimerTask* m_cancellableTimerTask; // NOT OWNED diff --git a/third_party/WebKit/Source/platform/TimerTest.cpp b/third_party/WebKit/Source/platform/TimerTest.cpp index 4585d95..9f7bd52 100644 --- a/third_party/WebKit/Source/platform/TimerTest.cpp +++ b/third_party/WebKit/Source/platform/TimerTest.cpp @@ -136,11 +136,6 @@ public: return nullptr; } - void postTimerTaskAt(const WebTraceLocation&, WebTaskRunner::Task* task, double monotonicTime) override - { - m_timerTasks.push(DelayedTask(task, (monotonicTime - monotonicallyIncreasingTime()) * 1000)); - } - void runUntilIdle() { while (m_timerTasks.size()) { @@ -698,80 +693,6 @@ TEST_F(TimerTest, AugmentRepeatInterval) EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 20.0, m_startTime + 40.0)); } -class MockTimerWithAlignment : public TimerBase { -public: - MockTimerWithAlignment() : m_lastFireTime(0.0), m_alignedFireTime(0.0) { } - - void fired() override - { - } - - double alignedFireTime(double fireTime) const override - { - m_lastFireTime = fireTime; - return m_alignedFireTime; - } - - void setAlignedFireTime(double alignedFireTime) - { - m_alignedFireTime = alignedFireTime; - } - - double lastFireTime() const - { - return m_lastFireTime; - } - -private: - mutable double m_lastFireTime; - double m_alignedFireTime; -}; - -TEST_F(TimerTest, TimerAlignment_OneShotZero) -{ - MockTimerWithAlignment timer; - timer.setAlignedFireTime(m_startTime + 1.0); - - timer.start(0.0, 0.0, BLINK_FROM_HERE); - - // The nextFireInterval gets overrriden. - EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval()); - EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); - EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); -} - -TEST_F(TimerTest, TimerAlignment_OneShotNonZero) -{ - MockTimerWithAlignment timer; - timer.setAlignedFireTime(m_startTime + 1.0); - - timer.start(0.5, 0.0, BLINK_FROM_HERE); - - // The nextFireInterval gets overrriden. - EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval()); - EXPECT_FLOAT_EQ(0.5, timer.nextUnalignedFireInterval()); - EXPECT_FLOAT_EQ(m_startTime + 0.5, timer.lastFireTime()); -} - -TEST_F(TimerTest, DidChangeAlignmentInterval) -{ - MockTimerWithAlignment timer; - timer.setAlignedFireTime(m_startTime + 1.0); - - timer.start(0.0, 0.0, BLINK_FROM_HERE); - - EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval()); - EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); - EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); - - timer.setAlignedFireTime(m_startTime); - timer.didChangeAlignmentInterval(monotonicallyIncreasingTime()); - - EXPECT_FLOAT_EQ(0.0, timer.nextFireInterval()); - EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); - EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); -} - TEST_F(TimerTest, RepeatingTimerDoesNotDrift) { Timer<TimerTest> timer(this, &TimerTest::recordNextFireTimeTask); diff --git a/third_party/WebKit/public/platform/WebScheduler.h b/third_party/WebKit/public/platform/WebScheduler.h index 0af83a2..aeeb8f4 100644 --- a/third_party/WebKit/public/platform/WebScheduler.h +++ b/third_party/WebKit/public/platform/WebScheduler.h @@ -60,15 +60,6 @@ public: // Takes ownership of |IdleTask|. Can be called from any thread. virtual void postIdleTaskAfterWakeup(const WebTraceLocation&, WebThread::IdleTask*) = 0; - // Schedule a timer task to be run on the the associated WebThread. Timer Tasks - // tasks usually have the default priority, but may be delayed - // when the user is interacting with the device. - // |monotonicTime| is in the timebase of WTF::monotonicallyIncreasingTime(). - // Takes ownership of |WebTaskRunner::Task|. Can be called from any thread. - // TODO(alexclarke): Move timer throttling for background pages to the - // chromium side and remove this. - virtual void postTimerTaskAt(const WebTraceLocation&, WebTaskRunner::Task*, double monotonicTime) = 0; - // Returns a WebTaskRunner for loading tasks. Can be called from any thread. virtual WebTaskRunner* loadingTaskRunner() = 0; |