diff options
Diffstat (limited to 'chromium/components/scheduler/base')
38 files changed, 0 insertions, 7061 deletions
diff --git a/chromium/components/scheduler/base/DEPS b/chromium/components/scheduler/base/DEPS deleted file mode 100644 index 5d86a1bc60e..00000000000 --- a/chromium/components/scheduler/base/DEPS +++ /dev/null @@ -1,9 +0,0 @@ -include_rules = [ - "+components/scheduler/scheduler_export.h", -] - -specific_include_rules = { - "(test_time_source|.*test)\.cc": [ - "+cc/test", - ], -} diff --git a/chromium/components/scheduler/base/cancelable_closure_holder.cc b/chromium/components/scheduler/base/cancelable_closure_holder.cc deleted file mode 100644 index 54c1462f5d3..00000000000 --- a/chromium/components/scheduler/base/cancelable_closure_holder.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 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/base/cancelable_closure_holder.h" - -namespace scheduler { - -CancelableClosureHolder::CancelableClosureHolder() {} - -CancelableClosureHolder::~CancelableClosureHolder() {} - -void CancelableClosureHolder::Reset(const base::Closure& callback) { - callback_ = callback; - cancelable_callback_.Reset(callback_); -} - -void CancelableClosureHolder::Cancel() { - DCHECK(!callback_.is_null()); - cancelable_callback_.Reset(callback_); -} - -const base::Closure& CancelableClosureHolder::callback() const { - DCHECK(!callback_.is_null()); - return cancelable_callback_.callback(); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/cancelable_closure_holder.h b/chromium/components/scheduler/base/cancelable_closure_holder.h deleted file mode 100644 index 5254cd490a5..00000000000 --- a/chromium/components/scheduler/base/cancelable_closure_holder.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 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_BASE_CANCELABLE_CLOSURE_HOLDER_H_ -#define COMPONENTS_SCHEDULER_BASE_CANCELABLE_CLOSURE_HOLDER_H_ - -#include "base/cancelable_callback.h" -#include "base/macros.h" - -namespace scheduler { - -// A CancelableClosureHolder is a CancelableCallback which resets its wrapped -// callback with a cached closure whenever it is canceled. -class CancelableClosureHolder { - public: - CancelableClosureHolder(); - ~CancelableClosureHolder(); - - // Resets the closure to be wrapped by the cancelable callback. Cancels any - // outstanding callbacks. - void Reset(const base::Closure& callback); - - // Cancels any outstanding closures returned by callback(). - void Cancel(); - - // Returns a callback that will be disabled by calling Cancel(). Callback - // must have been set using Reset() before calling this function. - const base::Closure& callback() const; - - private: - base::Closure callback_; - base::CancelableClosure cancelable_callback_; - - DISALLOW_COPY_AND_ASSIGN(CancelableClosureHolder); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_CANCELABLE_CLOSURE_HOLDER_H_ diff --git a/chromium/components/scheduler/base/enqueue_order.cc b/chromium/components/scheduler/base/enqueue_order.cc deleted file mode 100644 index dd04bbd4023..00000000000 --- a/chromium/components/scheduler/base/enqueue_order.cc +++ /dev/null @@ -1,20 +0,0 @@ -// 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/base/enqueue_order.h" - -namespace scheduler { -namespace internal { - -EnqueueOrderGenerator::EnqueueOrderGenerator() : enqueue_order_(0) {} - -EnqueueOrderGenerator::~EnqueueOrderGenerator() {} - -EnqueueOrder EnqueueOrderGenerator::GenerateNext() { - base::AutoLock lock(lock_); - return enqueue_order_++; -} - -} // namespace internal -} // namespace scheduler diff --git a/chromium/components/scheduler/base/enqueue_order.h b/chromium/components/scheduler/base/enqueue_order.h deleted file mode 100644 index 21001ffc284..00000000000 --- a/chromium/components/scheduler/base/enqueue_order.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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_BASE_ENQUEUE_ORDER_H_ -#define COMPONENTS_SCHEDULER_BASE_ENQUEUE_ORDER_H_ - -#include <stdint.h> - -#include "base/synchronization/lock.h" - -namespace scheduler { -namespace internal { - -using EnqueueOrder = uint64_t; - -class EnqueueOrderGenerator { - public: - EnqueueOrderGenerator(); - ~EnqueueOrderGenerator(); - - EnqueueOrder GenerateNext(); - - private: - base::Lock lock_; - EnqueueOrder enqueue_order_; -}; - -} // namespace internal -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_ENQUEUE_ORDER_H_ diff --git a/chromium/components/scheduler/base/lazy_now.cc b/chromium/components/scheduler/base/lazy_now.cc deleted file mode 100644 index 3492ca6766d..00000000000 --- a/chromium/components/scheduler/base/lazy_now.cc +++ /dev/null @@ -1,17 +0,0 @@ -// 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/base/lazy_now.h" - -#include "base/time/tick_clock.h" -#include "components/scheduler/base/task_queue_manager.h" - -namespace scheduler { -base::TimeTicks LazyNow::Now() { - if (now_.is_null()) - now_ = tick_clock_->NowTicks(); - return now_; -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/lazy_now.h b/chromium/components/scheduler/base/lazy_now.h deleted file mode 100644 index 959a2450947..00000000000 --- a/chromium/components/scheduler/base/lazy_now.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_BASE_LAZY_NOW_H_ -#define COMPONENTS_SCHEDULER_BASE_LAZY_NOW_H_ - -#include "base/time/time.h" -#include "components/scheduler/scheduler_export.h" - -namespace base { -class TickClock; -} - -namespace scheduler { - -// Now() is somewhat expensive so it makes sense not to call Now() unless we -// really need to. -class SCHEDULER_EXPORT LazyNow { - public: - explicit LazyNow(base::TimeTicks now) : tick_clock_(nullptr), now_(now) { - DCHECK(!now.is_null()); - } - - explicit LazyNow(base::TickClock* tick_clock) : tick_clock_(tick_clock) {} - - base::TimeTicks Now(); - - private: - base::TickClock* tick_clock_; // NOT OWNED - base::TimeTicks now_; -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_LAZY_NOW_H_ diff --git a/chromium/components/scheduler/base/pollable_thread_safe_flag.cc b/chromium/components/scheduler/base/pollable_thread_safe_flag.cc deleted file mode 100644 index 13d5b6f5ec8..00000000000 --- a/chromium/components/scheduler/base/pollable_thread_safe_flag.cc +++ /dev/null @@ -1,19 +0,0 @@ -// 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/base/pollable_thread_safe_flag.h" - -PollableThreadSafeFlag::PollableThreadSafeFlag(base::Lock* write_lock_) - : flag_(false), write_lock_(write_lock_) {} - -PollableThreadSafeFlag::~PollableThreadSafeFlag() {} - -void PollableThreadSafeFlag::SetWhileLocked(bool value) { - write_lock_->AssertAcquired(); - base::subtle::Release_Store(&flag_, value); -} - -bool PollableThreadSafeFlag::IsSet() const { - return base::subtle::Acquire_Load(&flag_) != false; -} diff --git a/chromium/components/scheduler/base/pollable_thread_safe_flag.h b/chromium/components/scheduler/base/pollable_thread_safe_flag.h deleted file mode 100644 index 8e37b690595..00000000000 --- a/chromium/components/scheduler/base/pollable_thread_safe_flag.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_BASE_POLLABLE_THREAD_SAFE_FLAG_H_ -#define COMPONENTS_SCHEDULER_BASE_POLLABLE_THREAD_SAFE_FLAG_H_ - -#include "base/atomicops.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" - -// A PollableThreadSafeFlag can be polled without requiring a lock, but can only -// be updated if a lock is held. This enables lock-free checking as to whether a -// condition has changed, while protecting operations which update the condition -// with a lock. You must ensure that the flag is only updated within the same -// lock-protected critical section as any other variables on which the condition -// depends. -class PollableThreadSafeFlag { - public: - explicit PollableThreadSafeFlag(base::Lock* write_lock); - ~PollableThreadSafeFlag(); - - // Set the flag. May only be called if |write_lock| is held. - void SetWhileLocked(bool value); - - // Returns true iff the flag is set to true. - bool IsSet() const; - - private: - base::subtle::Atomic32 flag_; - base::Lock* write_lock_; // Not owned. - - DISALLOW_COPY_AND_ASSIGN(PollableThreadSafeFlag); -}; - -#endif // COMPONENTS_SCHEDULER_BASE_POLLABLE_THREAD_SAFE_FLAG_H_ diff --git a/chromium/components/scheduler/base/real_time_domain.cc b/chromium/components/scheduler/base/real_time_domain.cc deleted file mode 100644 index a452b978e91..00000000000 --- a/chromium/components/scheduler/base/real_time_domain.cc +++ /dev/null @@ -1,73 +0,0 @@ -// 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/base/real_time_domain.h" - -#include "base/bind.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager.h" -#include "components/scheduler/base/task_queue_manager_delegate.h" - -namespace scheduler { - -RealTimeDomain::RealTimeDomain(const char* tracing_category) - : TimeDomain(nullptr), - tracing_category_(tracing_category), - task_queue_manager_(nullptr) {} - -RealTimeDomain::RealTimeDomain(TimeDomain::Observer* observer, - const char* tracing_category) - : TimeDomain(observer), - tracing_category_(tracing_category), - task_queue_manager_(nullptr) {} - -RealTimeDomain::~RealTimeDomain() {} - -void RealTimeDomain::OnRegisterWithTaskQueueManager( - TaskQueueManager* task_queue_manager) { - task_queue_manager_ = task_queue_manager; - DCHECK(task_queue_manager_); -} - -LazyNow RealTimeDomain::CreateLazyNow() const { - return task_queue_manager_->CreateLazyNow(); -} - -base::TimeTicks RealTimeDomain::Now() const { - return task_queue_manager_->delegate()->NowTicks(); -} - -void RealTimeDomain::RequestWakeup(base::TimeTicks now, base::TimeDelta delay) { - // NOTE this is only called if the scheduled runtime is sooner than any - // previously scheduled runtime, or there is no (outstanding) previously - // scheduled runtime. - task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, now, delay); -} - -bool RealTimeDomain::MaybeAdvanceTime() { - base::TimeTicks next_run_time; - if (!NextScheduledRunTime(&next_run_time)) - return false; - - base::TimeTicks now = Now(); - if (now >= next_run_time) - return true; // Causes DoWork to post a continuation. - - base::TimeDelta delay = next_run_time - now; - TRACE_EVENT1(tracing_category_, "RealTimeDomain::MaybeAdvanceTime", - "delay_ms", delay.InMillisecondsF()); - - // The next task is sometime in the future, make sure we schedule a DoWork to - // run it. - task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, now, delay); - return false; -} - -void RealTimeDomain::AsValueIntoInternal( - base::trace_event::TracedValue* state) const {} - -const char* RealTimeDomain::GetName() const { - return "RealTimeDomain"; -} -} // namespace scheduler diff --git a/chromium/components/scheduler/base/real_time_domain.h b/chromium/components/scheduler/base/real_time_domain.h deleted file mode 100644 index fa20ba7a077..00000000000 --- a/chromium/components/scheduler/base/real_time_domain.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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_BASE_REAL_TIME_DOMAIN_H_ -#define COMPONENTS_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_ - -#include <set> - -#include "base/macros.h" -#include "components/scheduler/base/time_domain.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { - -class SCHEDULER_EXPORT RealTimeDomain : public TimeDomain { - public: - explicit RealTimeDomain(const char* tracing_category); - RealTimeDomain(TimeDomain::Observer* observer, const char* tracing_category); - ~RealTimeDomain() override; - - // TimeDomain implementation: - LazyNow CreateLazyNow() const override; - base::TimeTicks Now() const override; - bool MaybeAdvanceTime() override; - const char* GetName() const override; - - protected: - void OnRegisterWithTaskQueueManager( - TaskQueueManager* task_queue_manager) override; - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override; - void AsValueIntoInternal( - base::trace_event::TracedValue* state) const override; - - private: - const char* tracing_category_; // NOT OWNED - TaskQueueManager* task_queue_manager_; // NOT OWNED - - DISALLOW_COPY_AND_ASSIGN(RealTimeDomain); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_ diff --git a/chromium/components/scheduler/base/task_queue.h b/chromium/components/scheduler/base/task_queue.h deleted file mode 100644 index b3a316362ce..00000000000 --- a/chromium/components/scheduler/base/task_queue.h +++ /dev/null @@ -1,216 +0,0 @@ -// 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_BASE_TASK_QUEUE_H_ -#define COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_H_ - -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/trace_event/trace_event.h" -#include "components/scheduler/scheduler_export.h" - -namespace base { -namespace trace_event { -class BlameContext; -} -} - -namespace scheduler { -class LazyNow; -class TimeDomain; - -class SCHEDULER_EXPORT TaskQueue : public base::SingleThreadTaskRunner { - public: - TaskQueue() {} - - // Unregisters the task queue after which no tasks posted to it will run and - // the TaskQueueManager's reference to it will be released soon. - virtual void UnregisterTaskQueue() = 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 - // private queues which perform control operations. - CONTROL_PRIORITY, - // Queues with high priority will be selected preferentially over normal or - // best effort queues. The selector will ensure that high priority queues - // cannot completely starve normal priority queues. - HIGH_PRIORITY, - // Queues with normal priority are the default. - NORMAL_PRIORITY, - // Queues with best effort priority will only be run if all other queues are - // empty. They can be starved by the other queues. - BEST_EFFORT_PRIORITY, - // Must be the last entry. - QUEUE_PRIORITY_COUNT, - FIRST_QUEUE_PRIORITY = CONTROL_PRIORITY, - }; - - // Keep TaskQueue::PumpPolicyToString in sync with this enum. - enum class PumpPolicy { - // Tasks posted to an incoming queue with an AUTO pump policy will be - // automatically scheduled for execution or transferred to the work queue - // automatically. - AUTO, - // Tasks posted to an incoming queue with an AFTER_WAKEUP pump policy - // will be scheduled for execution or transferred to the work queue - // automatically but only after another queue has executed a task. - AFTER_WAKEUP, - // Tasks posted to an incoming queue with a MANUAL will not be - // automatically scheduled for execution or transferred to the work queue. - // Instead, the selector should call PumpQueue() when necessary to bring - // in new tasks for execution. - MANUAL, - // Must be last entry. - PUMP_POLICY_COUNT, - FIRST_PUMP_POLICY = AUTO, - }; - - // Keep TaskQueue::WakeupPolicyToString in sync with this enum. - enum class WakeupPolicy { - // Tasks run on a queue with CAN_WAKE_OTHER_QUEUES wakeup policy can - // cause queues with the AFTER_WAKEUP PumpPolicy to be woken up. - CAN_WAKE_OTHER_QUEUES, - // Tasks run on a queue with DONT_WAKE_OTHER_QUEUES won't cause queues - // with the AFTER_WAKEUP PumpPolicy to be woken up. - DONT_WAKE_OTHER_QUEUES, - // Must be last entry. - WAKEUP_POLICY_COUNT, - FIRST_WAKEUP_POLICY = CAN_WAKE_OTHER_QUEUES, - }; - - // Options for constructing a TaskQueue. Once set the |name|, - // |should_monitor_quiescence| and |wakeup_policy| are immutable. The - // |pump_policy| can be mutated with |SetPumpPolicy()|. - struct Spec { - // Note |name| must have application lifetime. - explicit Spec(const char* name) - : name(name), - should_monitor_quiescence(false), - pump_policy(TaskQueue::PumpPolicy::AUTO), - wakeup_policy(TaskQueue::WakeupPolicy::CAN_WAKE_OTHER_QUEUES), - time_domain(nullptr), - should_notify_observers(true), - should_report_when_execution_blocked(false) {} - - Spec SetShouldMonitorQuiescence(bool should_monitor) { - should_monitor_quiescence = should_monitor; - return *this; - } - - Spec SetPumpPolicy(PumpPolicy policy) { - pump_policy = policy; - return *this; - } - - Spec SetWakeupPolicy(WakeupPolicy policy) { - wakeup_policy = policy; - return *this; - } - - Spec SetShouldNotifyObservers(bool run_observers) { - should_notify_observers = run_observers; - return *this; - } - - Spec SetTimeDomain(TimeDomain* domain) { - time_domain = domain; - return *this; - } - - // See TaskQueueManager::Observer::OnTriedToExecuteBlockedTask. - Spec SetShouldReportWhenExecutionBlocked(bool should_report) { - should_report_when_execution_blocked = should_report; - return *this; - } - - const char* name; - bool should_monitor_quiescence; - TaskQueue::PumpPolicy pump_policy; - TaskQueue::WakeupPolicy wakeup_policy; - TimeDomain* time_domain; - bool should_notify_observers; - bool should_report_when_execution_blocked; - }; - - // Enable or disable task execution for this queue. NOTE this must be called - // on the thread this TaskQueue was created by. - virtual void SetQueueEnabled(bool enabled) = 0; - - // NOTE this must be called on the thread this TaskQueue was created by. - virtual bool IsQueueEnabled() const = 0; - - // Returns true if the queue is completely empty. - virtual bool IsEmpty() const = 0; - - // Returns true if the queue has work that's ready to execute now, or if it - // would have if the queue was pumped. NOTE this must be called on the thread - // this TaskQueue was created by. - virtual bool HasPendingImmediateWork() const = 0; - - // Returns true if tasks can't run now but could if the queue was pumped. - virtual bool NeedsPumping() const = 0; - - // Can be called on any thread. - virtual const char* GetName() const = 0; - - // Set the priority of the queue to |priority|. NOTE this must be called on - // the thread this TaskQueue was created by. - virtual void SetQueuePriority(QueuePriority priority) = 0; - - // Returns the current queue priority. - virtual QueuePriority GetQueuePriority() const = 0; - - // Set the pumping policy of the queue to |pump_policy|. NOTE this must be - // called on the thread this TaskQueue was created by. - virtual void SetPumpPolicy(PumpPolicy pump_policy) = 0; - - // Returns the current PumpPolicy. NOTE this must be called on the thread this - // TaskQueue was created by. - virtual PumpPolicy GetPumpPolicy() const = 0; - - // Reloads new tasks from the incoming queue into the work queue, regardless - // of whether the work queue is empty or not. After this, the function ensures - // that the tasks in the work queue, if any, are scheduled for execution. - // - // This function only needs to be called if automatic pumping is disabled. - // By default automatic pumping is enabled for all queues. NOTE this must be - // called on the thread this TaskQueue was created by. - // - // The |may_post_dowork| parameter controls whether or not PumpQueue calls - // TaskQueueManager::MaybeScheduleImmediateWork. - // TODO(alexclarke): Add a base::RunLoop observer so we can get rid of - // |may_post_dowork|. - virtual void PumpQueue(LazyNow* lazy_now, bool may_post_dowork) = 0; - - // These functions can only be called on the same thread that the task queue - // manager executes its tasks on. - virtual void AddTaskObserver( - base::MessageLoop::TaskObserver* task_observer) = 0; - virtual void RemoveTaskObserver( - base::MessageLoop::TaskObserver* task_observer) = 0; - - // Set the blame context which is entered and left while executing tasks from - // this task queue. |blame_context| must be null or outlive this task queue. - // Must be called on the thread this TaskQueue was created by. - virtual void SetBlameContext( - base::trace_event::BlameContext* blame_context) = 0; - - // Removes the task queue from the previous TimeDomain and adds it to - // |domain|. This is a moderately expensive operation. - virtual void SetTimeDomain(TimeDomain* domain) = 0; - - // Returns the queue's current TimeDomain. Can be called from any thread. - virtual TimeDomain* GetTimeDomain() const = 0; - - protected: - ~TaskQueue() override {} - - DISALLOW_COPY_AND_ASSIGN(TaskQueue); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_H_ diff --git a/chromium/components/scheduler/base/task_queue_impl.cc b/chromium/components/scheduler/base/task_queue_impl.cc deleted file mode 100644 index 0dc5f648000..00000000000 --- a/chromium/components/scheduler/base/task_queue_impl.cc +++ /dev/null @@ -1,705 +0,0 @@ -// 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/base/task_queue_impl.h" - -#include "base/trace_event/blame_context.h" -#include "components/scheduler/base/task_queue_manager.h" -#include "components/scheduler/base/task_queue_manager_delegate.h" -#include "components/scheduler/base/time_domain.h" -#include "components/scheduler/base/work_queue.h" - -namespace scheduler { -namespace internal { - -TaskQueueImpl::TaskQueueImpl( - TaskQueueManager* task_queue_manager, - TimeDomain* time_domain, - const Spec& spec, - const char* disabled_by_default_tracing_category, - const char* disabled_by_default_verbose_tracing_category) - : thread_id_(base::PlatformThread::CurrentId()), - any_thread_(task_queue_manager, spec.pump_policy, time_domain), - name_(spec.name), - disabled_by_default_tracing_category_( - disabled_by_default_tracing_category), - disabled_by_default_verbose_tracing_category_( - disabled_by_default_verbose_tracing_category), - main_thread_only_(task_queue_manager, - spec.pump_policy, - this, - time_domain), - wakeup_policy_(spec.wakeup_policy), - should_monitor_quiescence_(spec.should_monitor_quiescence), - should_notify_observers_(spec.should_notify_observers), - should_report_when_execution_blocked_( - spec.should_report_when_execution_blocked) { - DCHECK(time_domain); - time_domain->RegisterQueue(this); -} - -TaskQueueImpl::~TaskQueueImpl() { -#if DCHECK_IS_ON() - base::AutoLock lock(any_thread_lock_); - // NOTE this check shouldn't fire because |TaskQueueManager::queues_| - // contains a strong reference to this TaskQueueImpl and the TaskQueueManager - // destructor calls UnregisterTaskQueue on all task queues. - DCHECK(any_thread().task_queue_manager == nullptr) - << "UnregisterTaskQueue must be called first!"; - -#endif -} - -TaskQueueImpl::Task::Task() - : PendingTask(tracked_objects::Location(), - base::Closure(), - base::TimeTicks(), - true), -#ifndef NDEBUG - enqueue_order_set_(false), -#endif - enqueue_order_(0) { - sequence_num = 0; -} - -TaskQueueImpl::Task::Task(const tracked_objects::Location& posted_from, - const base::Closure& task, - base::TimeTicks desired_run_time, - EnqueueOrder sequence_number, - bool nestable) - : PendingTask(posted_from, task, desired_run_time, nestable), -#ifndef NDEBUG - enqueue_order_set_(false), -#endif - enqueue_order_(0) { - sequence_num = sequence_number; -} - -TaskQueueImpl::Task::Task(const tracked_objects::Location& posted_from, - const base::Closure& task, - base::TimeTicks desired_run_time, - EnqueueOrder sequence_number, - bool nestable, - EnqueueOrder enqueue_order) - : PendingTask(posted_from, task, desired_run_time, nestable), -#ifndef NDEBUG - enqueue_order_set_(true), -#endif - enqueue_order_(enqueue_order) { - sequence_num = sequence_number; -} - -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) {} - -TaskQueueImpl::AnyThread::~AnyThread() {} - -TaskQueueImpl::MainThreadOnly::MainThreadOnly( - TaskQueueManager* task_queue_manager, - PumpPolicy pump_policy, - TaskQueueImpl* task_queue, - TimeDomain* time_domain) - : task_queue_manager(task_queue_manager), - pump_policy(pump_policy), - time_domain(time_domain), - delayed_work_queue(new WorkQueue(task_queue, "delayed")), - immediate_work_queue(new WorkQueue(task_queue, "immediate")), - set_index(0), - is_enabled(true), - blame_context(nullptr) {} - -TaskQueueImpl::MainThreadOnly::~MainThreadOnly() {} - -void TaskQueueImpl::UnregisterTaskQueue() { - base::AutoLock lock(any_thread_lock_); - if (main_thread_only().time_domain) - main_thread_only().time_domain->UnregisterQueue(this); - if (!any_thread().task_queue_manager) - return; - any_thread().time_domain = nullptr; - main_thread_only().time_domain = nullptr; - any_thread().task_queue_manager->UnregisterTaskQueue(this); - - any_thread().task_queue_manager = nullptr; - main_thread_only().task_queue_manager = nullptr; - main_thread_only().delayed_incoming_queue = std::priority_queue<Task>(); - any_thread().immediate_incoming_queue = std::queue<Task>(); - main_thread_only().immediate_work_queue.reset(); - main_thread_only().delayed_work_queue.reset(); -} - -bool TaskQueueImpl::RunsTasksOnCurrentThread() const { - base::AutoLock lock(any_thread_lock_); - return base::PlatformThread::CurrentId() == thread_id_; -} - -bool TaskQueueImpl::PostDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) { - if (delay.is_zero()) - return PostImmediateTaskImpl(from_here, task, TaskType::NORMAL); - - return PostDelayedTaskImpl(from_here, task, delay, TaskType::NORMAL); -} - -bool TaskQueueImpl::PostNonNestableDelayedTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) { - if (delay.is_zero()) - return PostImmediateTaskImpl(from_here, task, TaskType::NON_NESTABLE); - - return PostDelayedTaskImpl(from_here, task, delay, TaskType::NON_NESTABLE); -} - -bool TaskQueueImpl::PostImmediateTaskImpl( - const tracked_objects::Location& from_here, - const base::Closure& task, - TaskType task_type) { - base::AutoLock lock(any_thread_lock_); - if (!any_thread().task_queue_manager) - return false; - - EnqueueOrder sequence_number = - any_thread().task_queue_manager->GetNextSequenceNumber(); - - PushOntoImmediateIncomingQueueLocked( - Task(from_here, task, base::TimeTicks(), sequence_number, - task_type != TaskType::NON_NESTABLE, sequence_number)); - return true; -} - -bool TaskQueueImpl::PostDelayedTaskImpl( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay, - TaskType task_type) { - DCHECK_GT(delay, base::TimeDelta()); - if (base::PlatformThread::CurrentId() == thread_id_) { - // Lock-free fast path for delayed tasks posted from the main thread. - if (!main_thread_only().task_queue_manager) - return false; - - EnqueueOrder sequence_number = - main_thread_only().task_queue_manager->GetNextSequenceNumber(); - - base::TimeTicks time_domain_now = main_thread_only().time_domain->Now(); - base::TimeTicks time_domain_delayed_run_time = time_domain_now + delay; - PushOntoDelayedIncomingQueueFromMainThread( - Task(from_here, task, time_domain_delayed_run_time, sequence_number, - task_type != TaskType::NON_NESTABLE), - time_domain_now); - } else { - // NOTE posting a delayed task from a different thread is not expected to - // be common. This pathway is less optimal than perhaps it could be - // because it causes two main thread tasks to be run. Should this - // assumption prove to be false in future, we may need to revisit this. - base::AutoLock lock(any_thread_lock_); - if (!any_thread().task_queue_manager) - return false; - - EnqueueOrder sequence_number = - any_thread().task_queue_manager->GetNextSequenceNumber(); - - base::TimeTicks time_domain_now = any_thread().time_domain->Now(); - base::TimeTicks time_domain_delayed_run_time = time_domain_now + delay; - PushOntoDelayedIncomingQueueLocked( - Task(from_here, task, time_domain_delayed_run_time, sequence_number, - task_type != TaskType::NON_NESTABLE)); - } - return true; -} - -void TaskQueueImpl::PushOntoDelayedIncomingQueueFromMainThread( - const Task& pending_task, - base::TimeTicks now) { - main_thread_only().task_queue_manager->DidQueueTask(pending_task); - - // Schedule a later call to MoveReadyDelayedTasksToDelayedWorkQueue. - main_thread_only().delayed_incoming_queue.push(pending_task); - main_thread_only().time_domain->ScheduleDelayedWork( - this, pending_task.delayed_run_time, now); - TraceQueueSize(false); -} - -void TaskQueueImpl::PushOntoDelayedIncomingQueueLocked( - const Task& pending_task) { - any_thread().task_queue_manager->DidQueueTask(pending_task); - - int thread_hop_task_sequence_number = - any_thread().task_queue_manager->GetNextSequenceNumber(); - PushOntoImmediateIncomingQueueLocked(Task( - FROM_HERE, - base::Bind(&TaskQueueImpl::ScheduleDelayedWorkTask, this, pending_task), - base::TimeTicks(), thread_hop_task_sequence_number, false, - thread_hop_task_sequence_number)); -} - -void TaskQueueImpl::PushOntoImmediateIncomingQueueLocked( - const Task& pending_task) { - if (any_thread().immediate_incoming_queue.empty()) - any_thread().time_domain->RegisterAsUpdatableTaskQueue(this); - if (any_thread().pump_policy == PumpPolicy::AUTO && - any_thread().immediate_incoming_queue.empty()) { - any_thread().task_queue_manager->MaybeScheduleImmediateWork(FROM_HERE); - } - any_thread().task_queue_manager->DidQueueTask(pending_task); - any_thread().immediate_incoming_queue.push(pending_task); - TraceQueueSize(true); -} - -void TaskQueueImpl::ScheduleDelayedWorkTask(const Task& pending_task) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - main_thread_only().delayed_incoming_queue.push(pending_task); - main_thread_only().time_domain->ScheduleDelayedWork( - this, pending_task.delayed_run_time, - main_thread_only().time_domain->Now()); -} - -void TaskQueueImpl::SetQueueEnabled(bool enabled) { - if (main_thread_only().is_enabled == enabled) - return; - main_thread_only().is_enabled = enabled; - if (!main_thread_only().task_queue_manager) - return; - if (enabled) { - main_thread_only().task_queue_manager->selector_.EnableQueue(this); - } else { - main_thread_only().task_queue_manager->selector_.DisableQueue(this); - } -} - -bool TaskQueueImpl::IsQueueEnabled() const { - return main_thread_only().is_enabled; -} - -bool TaskQueueImpl::IsEmpty() const { - if (!main_thread_only().delayed_work_queue->Empty() || - !main_thread_only().immediate_work_queue->Empty()) { - return false; - } - - base::AutoLock lock(any_thread_lock_); - return any_thread().immediate_incoming_queue.empty() && - main_thread_only().delayed_incoming_queue.empty(); -} - -bool TaskQueueImpl::HasPendingImmediateWork() const { - if (!main_thread_only().delayed_work_queue->Empty() || - !main_thread_only().immediate_work_queue->Empty()) { - return true; - } - - return NeedsPumping(); -} - -bool TaskQueueImpl::NeedsPumping() const { - if (!main_thread_only().immediate_work_queue->Empty()) - return false; - - base::AutoLock lock(any_thread_lock_); - if (!any_thread().immediate_incoming_queue.empty()) - return true; - - // If there's no immediate Incoming work then we only need pumping if there - // is a delayed task that should be running now. - if (main_thread_only().delayed_incoming_queue.empty()) - return false; - - return main_thread_only().delayed_incoming_queue.top().delayed_run_time <= - main_thread_only().time_domain->CreateLazyNow().Now(); -} - -bool TaskQueueImpl::TaskIsOlderThanQueuedImmediateTasksLocked( - 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 - // here. - if (!task) - return true; - - // Return false if task is newer than the oldest immediate task. - if (!any_thread().immediate_incoming_queue.empty() && - task->enqueue_order() > - any_thread().immediate_incoming_queue.front().enqueue_order()) { - return false; - } - return true; -} - -bool TaskQueueImpl::TaskIsOlderThanQueuedDelayedTasks(const Task* task) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - // 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 - // here. - if (!task) - return true; - - EnqueueOrder enqueue_order; - if (!main_thread_only().delayed_work_queue->GetFrontTaskEnqueueOrder( - &enqueue_order)) { - return true; - } - - return task->enqueue_order() < enqueue_order; -} - -bool TaskQueueImpl::ShouldAutoPumpImmediateQueueLocked( - bool should_trigger_wakeup, - const Task* previous_task) { - if (main_thread_only().pump_policy == PumpPolicy::MANUAL) - return false; - if (main_thread_only().pump_policy == PumpPolicy::AFTER_WAKEUP && - (!should_trigger_wakeup || - TaskIsOlderThanQueuedImmediateTasksLocked(previous_task))) - return false; - return true; -} - -bool TaskQueueImpl::ShouldAutoPumpDelayedQueue(bool should_trigger_wakeup, - const Task* previous_task) { - if (main_thread_only().pump_policy == PumpPolicy::MANUAL) - return false; - if (main_thread_only().pump_policy == PumpPolicy::AFTER_WAKEUP && - (!should_trigger_wakeup || - TaskIsOlderThanQueuedDelayedTasks(previous_task))) - return false; - return true; -} - -void TaskQueueImpl::MoveReadyDelayedTasksToDelayedWorkQueue(LazyNow* lazy_now) { - // Enqueue all delayed tasks that should be running now. - while (!main_thread_only().delayed_incoming_queue.empty() && - main_thread_only().delayed_incoming_queue.top().delayed_run_time <= - lazy_now->Now()) { - // Note: the const_cast is needed because there is no direct way to move - // elements out of a priority queue. The queue must not be modified between - // the top() and the pop(). - main_thread_only().delayed_work_queue->PushAndSetEnqueueOrder( - std::move( - const_cast<Task&>(main_thread_only().delayed_incoming_queue.top())), - main_thread_only().task_queue_manager->GetNextSequenceNumber()); - main_thread_only().delayed_incoming_queue.pop(); - } -} - -void TaskQueueImpl::UpdateDelayedWorkQueue(LazyNow* lazy_now, - bool should_trigger_wakeup, - const Task* previous_task) { - if (!main_thread_only().task_queue_manager) - return; - if (!ShouldAutoPumpDelayedQueue(should_trigger_wakeup, previous_task)) - return; - MoveReadyDelayedTasksToDelayedWorkQueue(lazy_now); - TraceQueueSize(false); -} - -void TaskQueueImpl::UpdateImmediateWorkQueue(bool should_trigger_wakeup, - const Task* previous_task) { - DCHECK(main_thread_only().immediate_work_queue->Empty()); - base::AutoLock lock(any_thread_lock_); - if (!main_thread_only().task_queue_manager) - return; - if (!ShouldAutoPumpImmediateQueueLocked(should_trigger_wakeup, previous_task)) - return; - - main_thread_only().immediate_work_queue->SwapLocked( - any_thread().immediate_incoming_queue); - - // |any_thread().immediate_incoming_queue| is now empty so - // TimeDomain::UpdateQueues no longer needs to consider this queue for - // reloading. - main_thread_only().time_domain->UnregisterAsUpdatableTaskQueue(this); -} - -void TaskQueueImpl::TraceQueueSize(bool is_locked) const { - bool is_tracing; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(disabled_by_default_tracing_category_, - &is_tracing); - if (!is_tracing) - return; - - // It's only safe to access the work queues from the main thread. - // TODO(alexclarke): We should find another way of tracing this - if (base::PlatformThread::CurrentId() != thread_id_) - return; - - if (!is_locked) - any_thread_lock_.Acquire(); - else - any_thread_lock_.AssertAcquired(); - TRACE_COUNTER1(disabled_by_default_tracing_category_, GetName(), - any_thread().immediate_incoming_queue.size() + - main_thread_only().immediate_work_queue->Size() + - main_thread_only().delayed_work_queue->Size() + - main_thread_only().delayed_incoming_queue.size()); - if (!is_locked) - any_thread_lock_.Release(); -} - -void TaskQueueImpl::SetPumpPolicy(PumpPolicy pump_policy) { - base::AutoLock lock(any_thread_lock_); - if (pump_policy == PumpPolicy::AUTO && - any_thread().pump_policy != PumpPolicy::AUTO) { - LazyNow lazy_now(main_thread_only().time_domain->CreateLazyNow()); - PumpQueueLocked(&lazy_now, true); - } - any_thread().pump_policy = pump_policy; - main_thread_only().pump_policy = pump_policy; -} - -TaskQueue::PumpPolicy TaskQueueImpl::GetPumpPolicy() const { - return main_thread_only().pump_policy; -} - -void TaskQueueImpl::PumpQueueLocked(LazyNow* lazy_now, bool may_post_dowork) { - TRACE_EVENT1(disabled_by_default_tracing_category_, - "TaskQueueImpl::PumpQueueLocked", "queue", name_); - TaskQueueManager* task_queue_manager = any_thread().task_queue_manager; - if (!task_queue_manager) - return; - - MoveReadyDelayedTasksToDelayedWorkQueue(lazy_now); - - while (!any_thread().immediate_incoming_queue.empty()) { - main_thread_only().immediate_work_queue->Push( - std::move(any_thread().immediate_incoming_queue.front())); - any_thread().immediate_incoming_queue.pop(); - } - - // |immediate_incoming_queue| is now empty so TimeDomain::UpdateQueues no - // longer needs to consider this queue for reloading. - main_thread_only().time_domain->UnregisterAsUpdatableTaskQueue(this); - - if (main_thread_only().immediate_work_queue->Empty() && - main_thread_only().delayed_work_queue->Empty()) { - return; - } - - if (may_post_dowork) - task_queue_manager->MaybeScheduleImmediateWork(FROM_HERE); -} - -void TaskQueueImpl::PumpQueue(LazyNow* lazy_now, bool may_post_dowork) { - base::AutoLock lock(any_thread_lock_); - PumpQueueLocked(lazy_now, may_post_dowork); -} - -const char* TaskQueueImpl::GetName() const { - return name_; -} - -void TaskQueueImpl::SetQueuePriority(QueuePriority priority) { - if (!main_thread_only().task_queue_manager || priority == GetQueuePriority()) - return; - main_thread_only().task_queue_manager->selector_.SetQueuePriority(this, - priority); -} - -TaskQueueImpl::QueuePriority TaskQueueImpl::GetQueuePriority() const { - size_t set_index = immediate_work_queue()->work_queue_set_index(); - DCHECK_EQ(set_index, delayed_work_queue()->work_queue_set_index()); - return static_cast<TaskQueue::QueuePriority>(set_index); -} - -// static -const char* TaskQueueImpl::PumpPolicyToString( - TaskQueue::PumpPolicy pump_policy) { - switch (pump_policy) { - case TaskQueue::PumpPolicy::AUTO: - return "auto"; - case TaskQueue::PumpPolicy::AFTER_WAKEUP: - return "after_wakeup"; - case TaskQueue::PumpPolicy::MANUAL: - return "manual"; - default: - NOTREACHED(); - return nullptr; - } -} - -// static -const char* TaskQueueImpl::WakeupPolicyToString( - TaskQueue::WakeupPolicy wakeup_policy) { - switch (wakeup_policy) { - case TaskQueue::WakeupPolicy::CAN_WAKE_OTHER_QUEUES: - return "can_wake_other_queues"; - case TaskQueue::WakeupPolicy::DONT_WAKE_OTHER_QUEUES: - return "dont_wake_other_queues"; - default: - NOTREACHED(); - return nullptr; - } -} - -// static -const char* TaskQueueImpl::PriorityToString(QueuePriority priority) { - switch (priority) { - case CONTROL_PRIORITY: - return "control"; - case HIGH_PRIORITY: - return "high"; - case NORMAL_PRIORITY: - return "normal"; - case BEST_EFFORT_PRIORITY: - return "best_effort"; - default: - NOTREACHED(); - return nullptr; - } -} - -void TaskQueueImpl::AsValueInto(base::trace_event::TracedValue* state) const { - base::AutoLock lock(any_thread_lock_); - state->BeginDictionary(); - state->SetString("name", GetName()); - state->SetBoolean("enabled", main_thread_only().is_enabled); - state->SetString("time_domain_name", - main_thread_only().time_domain->GetName()); - state->SetString("pump_policy", PumpPolicyToString(any_thread().pump_policy)); - state->SetString("wakeup_policy", WakeupPolicyToString(wakeup_policy_)); - bool verbose_tracing_enabled = false; - TRACE_EVENT_CATEGORY_GROUP_ENABLED( - disabled_by_default_verbose_tracing_category_, &verbose_tracing_enabled); - state->SetInteger("immediate_incoming_queue_size", - any_thread().immediate_incoming_queue.size()); - state->SetInteger("delayed_incoming_queue_size", - main_thread_only().delayed_incoming_queue.size()); - state->SetInteger("immediate_work_queue_size", - main_thread_only().immediate_work_queue->Size()); - state->SetInteger("delayed_work_queue_size", - main_thread_only().delayed_work_queue->Size()); - if (!main_thread_only().delayed_incoming_queue.empty()) { - base::TimeDelta delay_to_next_task = - (main_thread_only().delayed_incoming_queue.top().delayed_run_time - - main_thread_only().time_domain->CreateLazyNow().Now()); - state->SetDouble("delay_to_next_task_ms", - delay_to_next_task.InMillisecondsF()); - } - if (verbose_tracing_enabled) { - state->BeginArray("immediate_incoming_queue"); - QueueAsValueInto(any_thread().immediate_incoming_queue, state); - state->EndArray(); - state->BeginArray("delayed_work_queue"); - main_thread_only().delayed_work_queue->AsValueInto(state); - state->EndArray(); - state->BeginArray("immediate_work_queue"); - main_thread_only().immediate_work_queue->AsValueInto(state); - state->EndArray(); - state->BeginArray("delayed_incoming_queue"); - QueueAsValueInto(main_thread_only().delayed_incoming_queue, state); - state->EndArray(); - } - state->SetString("priority", PriorityToString(GetQueuePriority())); - state->EndDictionary(); -} - -void TaskQueueImpl::AddTaskObserver( - base::MessageLoop::TaskObserver* task_observer) { - main_thread_only().task_observers.AddObserver(task_observer); -} - -void TaskQueueImpl::RemoveTaskObserver( - base::MessageLoop::TaskObserver* task_observer) { - main_thread_only().task_observers.RemoveObserver(task_observer); -} - -void TaskQueueImpl::NotifyWillProcessTask( - const base::PendingTask& pending_task) { - DCHECK(should_notify_observers_); - if (main_thread_only().blame_context) - main_thread_only().blame_context->Enter(); - FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, - main_thread_only().task_observers, - WillProcessTask(pending_task)); -} - -void TaskQueueImpl::NotifyDidProcessTask( - const base::PendingTask& pending_task) { - DCHECK(should_notify_observers_); - FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, - main_thread_only().task_observers, - DidProcessTask(pending_task)); - if (main_thread_only().blame_context) - main_thread_only().blame_context->Leave(); -} - -void TaskQueueImpl::SetTimeDomain(TimeDomain* time_domain) { - base::AutoLock lock(any_thread_lock_); - DCHECK(time_domain); - // NOTE this is similar to checking |any_thread().task_queue_manager| but the - // TaskQueueSelectorTests constructs TaskQueueImpl directly with a null - // task_queue_manager. Instead we check |any_thread().time_domain| which is - // another way of asserting that UnregisterTaskQueue has not been called. - DCHECK(any_thread().time_domain); - if (!any_thread().time_domain) - return; - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (time_domain == main_thread_only().time_domain) - return; - - main_thread_only().time_domain->MigrateQueue(this, time_domain); - main_thread_only().time_domain = time_domain; - any_thread().time_domain = time_domain; -} - -TimeDomain* TaskQueueImpl::GetTimeDomain() const { - if (base::PlatformThread::CurrentId() == thread_id_) - return main_thread_only().time_domain; - - base::AutoLock lock(any_thread_lock_); - return any_thread().time_domain; -} - -void TaskQueueImpl::SetBlameContext( - base::trace_event::BlameContext* blame_context) { - main_thread_only().blame_context = blame_context; -} - -// static -void TaskQueueImpl::QueueAsValueInto(const std::queue<Task>& queue, - base::trace_event::TracedValue* state) { - std::queue<Task> queue_copy(queue); - while (!queue_copy.empty()) { - TaskAsValueInto(queue_copy.front(), state); - queue_copy.pop(); - } -} - -// static -void TaskQueueImpl::QueueAsValueInto(const std::priority_queue<Task>& queue, - base::trace_event::TracedValue* state) { - std::priority_queue<Task> queue_copy(queue); - while (!queue_copy.empty()) { - TaskAsValueInto(queue_copy.top(), state); - queue_copy.pop(); - } -} - -// static -void TaskQueueImpl::TaskAsValueInto(const Task& task, - base::trace_event::TracedValue* state) { - state->BeginDictionary(); - state->SetString("posted_from", task.posted_from.ToString()); -#ifndef NDEBUG - if (task.enqueue_order_set()) - state->SetInteger("enqueue_order", task.enqueue_order()); -#else - state->SetInteger("enqueue_order", task.enqueue_order()); -#endif - state->SetInteger("sequence_num", task.sequence_num); - state->SetBoolean("nestable", task.nestable); - state->SetBoolean("is_high_res", task.is_high_res); - state->SetDouble( - "delayed_run_time", - (task.delayed_run_time - base::TimeTicks()).InMicroseconds() / 1000.0L); - state->EndDictionary(); -} - -} // namespace internal -} // namespace scheduler diff --git a/chromium/components/scheduler/base/task_queue_impl.h b/chromium/components/scheduler/base/task_queue_impl.h deleted file mode 100644 index 0e7f007204a..00000000000 --- a/chromium/components/scheduler/base/task_queue_impl.h +++ /dev/null @@ -1,309 +0,0 @@ -// 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 CONTENT_RENDERER_SCHEDULER_BASE_TASK_QUEUE_IMPL_H_ -#define CONTENT_RENDERER_SCHEDULER_BASE_TASK_QUEUE_IMPL_H_ - -#include <stddef.h> - -#include <memory> -#include <set> - -#include "base/macros.h" -#include "base/pending_task.h" -#include "base/threading/thread_checker.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_argument.h" -#include "components/scheduler/base/enqueue_order.h" -#include "components/scheduler/base/task_queue.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { -class LazyNow; -class TimeDomain; -class TaskQueueManager; - -namespace internal { -class WorkQueue; -class WorkQueueSets; - -class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue { - public: - TaskQueueImpl(TaskQueueManager* task_queue_manager, - TimeDomain* time_domain, - const Spec& spec, - const char* disabled_by_default_tracing_category, - const char* disabled_by_default_verbose_tracing_category); - - class SCHEDULER_EXPORT Task : public base::PendingTask { - public: - Task(); - Task(const tracked_objects::Location& posted_from, - const base::Closure& task, - base::TimeTicks desired_run_time, - EnqueueOrder sequence_number, - bool nestable); - - Task(const tracked_objects::Location& posted_from, - const base::Closure& task, - base::TimeTicks desired_run_time, - EnqueueOrder sequence_number, - bool nestable, - EnqueueOrder enqueue_order); - - EnqueueOrder enqueue_order() const { -#ifndef NDEBUG - DCHECK(enqueue_order_set_); -#endif - return enqueue_order_; - } - - void set_enqueue_order(EnqueueOrder enqueue_order) { -#ifndef NDEBUG - DCHECK(!enqueue_order_set_); - enqueue_order_set_ = true; -#endif - enqueue_order_ = enqueue_order; - } - -#ifndef NDEBUG - bool enqueue_order_set() const { return enqueue_order_set_; } -#endif - - private: -#ifndef NDEBUG - bool enqueue_order_set_; -#endif - // Similar to sequence number, but the |enqueue_order| is set by - // EnqueueTasksLocked and is not initially defined for delayed tasks until - // they are enqueued on the |immediate_incoming_queue_|. - EnqueueOrder enqueue_order_; - }; - - // TaskQueue implementation. - void UnregisterTaskQueue() override; - bool RunsTasksOnCurrentThread() const override; - bool PostDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) override; - bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) override; - - void SetQueueEnabled(bool enabled) override; - bool IsQueueEnabled() const override; - bool IsEmpty() const override; - bool HasPendingImmediateWork() const override; - bool NeedsPumping() const override; - void SetQueuePriority(QueuePriority priority) override; - QueuePriority GetQueuePriority() const override; - void PumpQueue(LazyNow* lazy_now, bool may_post_dowork) override; - void SetPumpPolicy(PumpPolicy pump_policy) override; - PumpPolicy GetPumpPolicy() const override; - void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override; - void RemoveTaskObserver( - base::MessageLoop::TaskObserver* task_observer) override; - void SetTimeDomain(TimeDomain* time_domain) override; - TimeDomain* GetTimeDomain() const override; - void SetBlameContext(base::trace_event::BlameContext* blame_context) override; - - void UpdateImmediateWorkQueue(bool should_trigger_wakeup, - const Task* previous_task); - void UpdateDelayedWorkQueue(LazyNow* lazy_now, - bool should_trigger_wakeup, - const Task* previous_task); - - WakeupPolicy wakeup_policy() const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - return wakeup_policy_; - } - - const char* GetName() const override; - - void AsValueInto(base::trace_event::TracedValue* state) const; - - bool GetQuiescenceMonitored() const { return should_monitor_quiescence_; } - bool GetShouldNotifyObservers() const { - return should_notify_observers_; - } - - void NotifyWillProcessTask(const base::PendingTask& pending_task); - void NotifyDidProcessTask(const base::PendingTask& pending_task); - - // Can be called on any thread. - static const char* PumpPolicyToString(TaskQueue::PumpPolicy pump_policy); - - // Can be called on any thread. - static const char* WakeupPolicyToString( - TaskQueue::WakeupPolicy wakeup_policy); - - // Can be called on any thread. - static const char* PriorityToString(TaskQueue::QueuePriority priority); - - WorkQueue* delayed_work_queue() { - return main_thread_only().delayed_work_queue.get(); - } - - const WorkQueue* delayed_work_queue() const { - return main_thread_only().delayed_work_queue.get(); - } - - WorkQueue* immediate_work_queue() { - return main_thread_only().immediate_work_queue.get(); - } - - const WorkQueue* immediate_work_queue() const { - return main_thread_only().immediate_work_queue.get(); - } - - bool should_report_when_execution_blocked() const { - return should_report_when_execution_blocked_; - } - - private: - friend class WorkQueue; - - enum class TaskType { - NORMAL, - NON_NESTABLE, - }; - - struct AnyThread { - AnyThread(TaskQueueManager* task_queue_manager, - PumpPolicy pump_policy, - TimeDomain* time_domain); - ~AnyThread(); - - // TaskQueueManager, PumpPolicy and TimeDomain are maintained in two copies: - // inside AnyThread and inside MainThreadOnly. They can be changed only from - // main thread, so it should be locked before accessing from other threads. - TaskQueueManager* task_queue_manager; - PumpPolicy pump_policy; - TimeDomain* time_domain; - - std::queue<Task> immediate_incoming_queue; - }; - - struct MainThreadOnly { - MainThreadOnly(TaskQueueManager* task_queue_manager, - PumpPolicy pump_policy, - TaskQueueImpl* task_queue, - TimeDomain* time_domain); - ~MainThreadOnly(); - - // Another copy of TaskQueueManager, PumpPolicy and TimeDomain for lock-free - // access from the main thread. See description inside struct AnyThread for - // details. - TaskQueueManager* task_queue_manager; - PumpPolicy pump_policy; - TimeDomain* time_domain; - - std::unique_ptr<WorkQueue> delayed_work_queue; - std::unique_ptr<WorkQueue> immediate_work_queue; - std::priority_queue<Task> delayed_incoming_queue; - base::ObserverList<base::MessageLoop::TaskObserver> task_observers; - size_t set_index; - bool is_enabled; - base::trace_event::BlameContext* blame_context; // Not owned. - }; - - ~TaskQueueImpl() override; - - bool PostImmediateTaskImpl(const tracked_objects::Location& from_here, - const base::Closure& task, - TaskType task_type); - bool PostDelayedTaskImpl(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay, - TaskType task_type); - - // Push the task onto the |delayed_incoming_queue|. Lock-free main thread - // only fast path. - void PushOntoDelayedIncomingQueueFromMainThread(const Task& pending_task, - base::TimeTicks now); - - // Push the task onto the |delayed_incoming_queue|. Slow path from other - // threads. - void PushOntoDelayedIncomingQueueLocked(const Task& pending_task); - - void ScheduleDelayedWorkTask(const Task& pending_task); - - // Enqueues any delayed tasks which should be run now on the - // |delayed_work_queue|. Must be called from the main thread. - void MoveReadyDelayedTasksToDelayedWorkQueue(LazyNow* lazy_now); - - void MoveReadyImmediateTasksToImmediateWorkQueueLocked(); - - // Note this does nothing if its not called from the main thread. - void PumpQueueLocked(LazyNow* lazy_now, bool may_post_dowork); - - // Returns true if |task| is older than the oldest incoming immediate task. - // NOTE |any_thread_lock_| must be locked. - bool TaskIsOlderThanQueuedImmediateTasksLocked(const Task* task); - - // Returns true if |task| is older than the oldest delayed task. Must be - // called from the main thread. - bool TaskIsOlderThanQueuedDelayedTasks(const Task* task); - - // NOTE |any_thread_lock_| must be locked. - bool ShouldAutoPumpImmediateQueueLocked(bool should_trigger_wakeup, - const Task* previous_task); - - // Must be called from the main thread. - bool ShouldAutoPumpDelayedQueue(bool should_trigger_wakeup, - const Task* previous_task); - - // Push the task onto the |immediate_incoming_queue| and for auto pumped - // queues it calls MaybePostDoWorkOnMainRunner if the Incoming queue was - // empty. - void PushOntoImmediateIncomingQueueLocked(const Task& pending_task); - - void TraceQueueSize(bool is_locked) const; - static void QueueAsValueInto(const std::queue<Task>& queue, - base::trace_event::TracedValue* state); - static void QueueAsValueInto(const std::priority_queue<Task>& queue, - base::trace_event::TracedValue* state); - static void TaskAsValueInto(const Task& task, - base::trace_event::TracedValue* state); - - const base::PlatformThreadId thread_id_; - - mutable base::Lock any_thread_lock_; - AnyThread any_thread_; - struct AnyThread& any_thread() { - any_thread_lock_.AssertAcquired(); - return any_thread_; - } - const struct AnyThread& any_thread() const { - any_thread_lock_.AssertAcquired(); - return any_thread_; - } - - const char* name_; - const char* disabled_by_default_tracing_category_; - const char* disabled_by_default_verbose_tracing_category_; - - base::ThreadChecker main_thread_checker_; - MainThreadOnly main_thread_only_; - MainThreadOnly& main_thread_only() { - DCHECK(main_thread_checker_.CalledOnValidThread()); - return main_thread_only_; - } - const MainThreadOnly& main_thread_only() const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - return main_thread_only_; - } - - const WakeupPolicy wakeup_policy_; - const bool should_monitor_quiescence_; - const bool should_notify_observers_; - const bool should_report_when_execution_blocked_; - - DISALLOW_COPY_AND_ASSIGN(TaskQueueImpl); -}; - -} // namespace internal -} // namespace scheduler - -#endif // CONTENT_RENDERER_SCHEDULER_BASE_TASK_QUEUE_IMPL_H_ diff --git a/chromium/components/scheduler/base/task_queue_manager.cc b/chromium/components/scheduler/base/task_queue_manager.cc deleted file mode 100644 index 36f08de540f..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager.cc +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright 2014 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/base/task_queue_manager.h" - -#include <queue> -#include <set> - -#include "base/bind.h" -#include "base/metrics/histogram_macros.h" -#include "base/trace_event/trace_event.h" -#include "components/scheduler/base/real_time_domain.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager_delegate.h" -#include "components/scheduler/base/task_queue_selector.h" -#include "components/scheduler/base/work_queue.h" -#include "components/scheduler/base/work_queue_sets.h" - -namespace scheduler { - -namespace { -const size_t kRecordRecordTaskDelayHistogramsEveryNTasks = 10; - -void RecordDelayedTaskLateness(base::TimeDelta lateness) { - UMA_HISTOGRAM_TIMES("RendererScheduler.TaskQueueManager.DelayedTaskLateness", - lateness); -} - -void RecordImmediateTaskQueueingDuration(tracked_objects::Duration duration) { - UMA_HISTOGRAM_TIMES( - "RendererScheduler.TaskQueueManager.ImmediateTaskQueueingDuration", - base::TimeDelta::FromMilliseconds(duration.InMilliseconds())); -} -} - -TaskQueueManager::TaskQueueManager( - scoped_refptr<TaskQueueManagerDelegate> delegate, - const char* tracing_category, - const char* disabled_by_default_tracing_category, - const char* disabled_by_default_verbose_tracing_category) - : real_time_domain_(new RealTimeDomain(tracing_category)), - delegate_(delegate), - task_was_run_on_quiescence_monitored_queue_(false), - work_batch_size_(1), - task_count_(0), - tracing_category_(tracing_category), - disabled_by_default_tracing_category_( - disabled_by_default_tracing_category), - disabled_by_default_verbose_tracing_category_( - disabled_by_default_verbose_tracing_category), - currently_executing_task_queue_(nullptr), - observer_(nullptr), - deletion_sentinel_(new DeletionSentinel()), - weak_factory_(this) { - DCHECK(delegate->RunsTasksOnCurrentThread()); - TRACE_EVENT_OBJECT_CREATED_WITH_ID(disabled_by_default_tracing_category, - "TaskQueueManager", this); - selector_.SetTaskQueueSelectorObserver(this); - - from_main_thread_immediate_do_work_closure_ = - base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), - base::TimeTicks(), true); - from_other_thread_immediate_do_work_closure_ = - base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), - base::TimeTicks(), false); - - // TODO(alexclarke): Change this to be a parameter that's passed in. - RegisterTimeDomain(real_time_domain_.get()); -} - -TaskQueueManager::~TaskQueueManager() { - TRACE_EVENT_OBJECT_DELETED_WITH_ID(disabled_by_default_tracing_category_, - "TaskQueueManager", this); - - while (!queues_.empty()) - (*queues_.begin())->UnregisterTaskQueue(); - - selector_.SetTaskQueueSelectorObserver(nullptr); -} - -void TaskQueueManager::RegisterTimeDomain(TimeDomain* time_domain) { - time_domains_.insert(time_domain); - time_domain->OnRegisterWithTaskQueueManager(this); -} - -void TaskQueueManager::UnregisterTimeDomain(TimeDomain* time_domain) { - time_domains_.erase(time_domain); -} - -scoped_refptr<internal::TaskQueueImpl> TaskQueueManager::NewTaskQueue( - const TaskQueue::Spec& spec) { - TRACE_EVENT1(tracing_category_, - "TaskQueueManager::NewTaskQueue", "queue_name", spec.name); - DCHECK(main_thread_checker_.CalledOnValidThread()); - TimeDomain* time_domain = - spec.time_domain ? spec.time_domain : real_time_domain_.get(); - 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_, - disabled_by_default_verbose_tracing_category_))); - queues_.insert(queue); - selector_.AddQueue(queue.get()); - return queue; -} - -void TaskQueueManager::SetObserver(Observer* observer) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - observer_ = observer; -} - -void TaskQueueManager::UnregisterTaskQueue( - scoped_refptr<internal::TaskQueueImpl> task_queue) { - TRACE_EVENT1(tracing_category_, - "TaskQueueManager::UnregisterTaskQueue", "queue_name", - task_queue->GetName()); - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (observer_) - observer_->OnUnregisterTaskQueue(task_queue); - - // Add |task_queue| to |queues_to_delete_| so we can prevent it from being - // freed while any of our structures hold hold a raw pointer to it. - queues_to_delete_.insert(task_queue); - queues_.erase(task_queue); - selector_.RemoveQueue(task_queue.get()); -} - -void TaskQueueManager::UpdateWorkQueues( - bool should_trigger_wakeup, - const internal::TaskQueueImpl::Task* previous_task) { - TRACE_EVENT0(disabled_by_default_tracing_category_, - "TaskQueueManager::UpdateWorkQueues"); - - for (TimeDomain* time_domain : time_domains_) { - time_domain->UpdateWorkQueues(should_trigger_wakeup, previous_task); - } -} - -void TaskQueueManager::MaybeScheduleImmediateWork( - const tracked_objects::Location& from_here) { - bool on_main_thread = delegate_->BelongsToCurrentThread(); - // De-duplicate DoWork posts. - if (on_main_thread) { - if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) { - return; - } - delegate_->PostTask(from_here, from_main_thread_immediate_do_work_closure_); - } else { - { - base::AutoLock lock(other_thread_lock_); - if (!other_thread_pending_wakeups_.insert(base::TimeTicks()).second) - return; - } - delegate_->PostTask(from_here, - from_other_thread_immediate_do_work_closure_); - } -} - -void TaskQueueManager::MaybeScheduleDelayedWork( - const tracked_objects::Location& from_here, - base::TimeTicks now, - base::TimeDelta delay) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK_GE(delay, base::TimeDelta()); - base::TimeTicks run_time = now + delay; - // De-duplicate DoWork posts. - if (!main_thread_pending_wakeups_.insert(run_time).second) - return; - delegate_->PostDelayedTask( - from_here, base::Bind(&TaskQueueManager::DoWork, - weak_factory_.GetWeakPtr(), run_time, true), - delay); -} - -void TaskQueueManager::DoWork(base::TimeTicks run_time, bool from_main_thread) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", - "from_main_thread", from_main_thread); - if (from_main_thread) { - main_thread_pending_wakeups_.erase(run_time); - } else { - base::AutoLock lock(other_thread_lock_); - other_thread_pending_wakeups_.erase(run_time); - } - - if (!delegate_->IsNested()) - queues_to_delete_.clear(); - - // Pass false and nullptr to UpdateWorkQueues here to prevent waking up a - // pump-after-wakeup queue. - UpdateWorkQueues(false, nullptr); - - internal::TaskQueueImpl::Task previous_task; - for (int i = 0; i < work_batch_size_; i++) { - internal::WorkQueue* work_queue; - if (!SelectWorkQueueToService(&work_queue)) { - break; - } - - bool should_trigger_wakeup = work_queue->task_queue()->wakeup_policy() == - TaskQueue::WakeupPolicy::CAN_WAKE_OTHER_QUEUES; - switch (ProcessTaskFromWorkQueue(work_queue, &previous_task)) { - case ProcessTaskResult::DEFERRED: - // If a task was deferred, try again with another task. Note that this - // means deferred tasks (i.e. non-nestable tasks) will never trigger - // queue wake-ups. - continue; - case ProcessTaskResult::EXECUTED: - break; - case ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED: - return; // The TaskQueueManager got deleted, we must bail out. - } - work_queue = nullptr; // The queue may have been unregistered. - - UpdateWorkQueues(should_trigger_wakeup, &previous_task); - - // Only run a single task per batch in nested run loops so that we can - // properly exit the nested loop when someone calls RunLoop::Quit(). - if (delegate_->IsNested()) - break; - } - - // TODO(alexclarke): Consider refactoring the above loop to terminate only - // when there's no more work left to be done, rather than posting a - // continuation task. - if (!selector_.EnabledWorkQueuesEmpty() || TryAdvanceTimeDomains()) - MaybeScheduleImmediateWork(FROM_HERE); -} - -bool TaskQueueManager::TryAdvanceTimeDomains() { - bool can_advance = false; - for (TimeDomain* time_domain : time_domains_) { - can_advance |= time_domain->MaybeAdvanceTime(); - } - return can_advance; -} - -bool TaskQueueManager::SelectWorkQueueToService( - internal::WorkQueue** out_work_queue) { - bool should_run = selector_.SelectWorkQueueToService(out_work_queue); - TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( - disabled_by_default_tracing_category_, "TaskQueueManager", this, - AsValueWithSelectorResult(should_run, *out_work_queue)); - return should_run; -} - -void TaskQueueManager::DidQueueTask( - const internal::TaskQueueImpl::Task& pending_task) { - task_annotator_.DidQueueTask("TaskQueueManager::PostTask", pending_task); -} - -TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue( - internal::WorkQueue* work_queue, - internal::TaskQueueImpl::Task* out_previous_task) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - scoped_refptr<DeletionSentinel> protect(deletion_sentinel_); - internal::TaskQueueImpl* queue = work_queue->task_queue(); - - if (queue->GetQuiescenceMonitored()) - task_was_run_on_quiescence_monitored_queue_ = true; - - internal::TaskQueueImpl::Task pending_task = - work_queue->TakeTaskFromWorkQueue(); - if (!pending_task.nestable && delegate_->IsNested()) { - // Defer non-nestable work to the main task runner. NOTE these tasks can be - // arbitrarily delayed so the additional delay should not be a problem. - // TODO(skyostil): Figure out a way to not forget which task queue the - // task is associated with. See http://crbug.com/522843. - delegate_->PostNonNestableTask(pending_task.posted_from, pending_task.task); - return ProcessTaskResult::DEFERRED; - } - - MaybeRecordTaskDelayHistograms(pending_task, queue); - - TRACE_TASK_EXECUTION("TaskQueueManager::ProcessTaskFromWorkQueue", - pending_task); - if (queue->GetShouldNotifyObservers()) { - FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_, - WillProcessTask(pending_task)); - queue->NotifyWillProcessTask(pending_task); - } - TRACE_EVENT1(tracing_category_, - "TaskQueueManager::RunTask", "queue", queue->GetName()); - // NOTE when TaskQueues get unregistered a reference ends up getting retained - // by |queues_to_delete_| which is cleared at the top of |DoWork|. This means - // we are OK to use raw pointers here. - internal::TaskQueueImpl* prev_executing_task_queue = - currently_executing_task_queue_; - currently_executing_task_queue_ = queue; - task_annotator_.RunTask("TaskQueueManager::PostTask", pending_task); - - // Detect if the TaskQueueManager just got deleted. If this happens we must - // not access any member variables after this point. - if (protect->HasOneRef()) - return ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED; - - currently_executing_task_queue_ = prev_executing_task_queue; - - if (queue->GetShouldNotifyObservers()) { - FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_, - DidProcessTask(pending_task)); - queue->NotifyDidProcessTask(pending_task); - } - - pending_task.task.Reset(); - *out_previous_task = pending_task; - return ProcessTaskResult::EXECUTED; -} - -void TaskQueueManager::MaybeRecordTaskDelayHistograms( - const internal::TaskQueueImpl::Task& pending_task, - const internal::TaskQueueImpl* queue) { - if ((task_count_++ % kRecordRecordTaskDelayHistogramsEveryNTasks) != 0) - return; - - // Record delayed task lateness and immediate task queueing durations, but - // only for auto-pumped queues. Manually pumped and after wakeup queues can - // have arbitarially large delayes, which would cloud any analysis. - if (queue->GetPumpPolicy() == TaskQueue::PumpPolicy::AUTO) { - if (!pending_task.delayed_run_time.is_null()) { - RecordDelayedTaskLateness(delegate_->NowTicks() - - pending_task.delayed_run_time); - } else if (!pending_task.time_posted.is_null()) { - RecordImmediateTaskQueueingDuration(tracked_objects::TrackedTime::Now() - - pending_task.time_posted); - } - } -} - -bool TaskQueueManager::RunsTasksOnCurrentThread() const { - return delegate_->RunsTasksOnCurrentThread(); -} - -void TaskQueueManager::SetWorkBatchSize(int work_batch_size) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK_GE(work_batch_size, 1); - work_batch_size_ = work_batch_size; -} - -void TaskQueueManager::AddTaskObserver( - base::MessageLoop::TaskObserver* task_observer) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - task_observers_.AddObserver(task_observer); -} - -void TaskQueueManager::RemoveTaskObserver( - base::MessageLoop::TaskObserver* task_observer) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - task_observers_.RemoveObserver(task_observer); -} - -bool TaskQueueManager::GetAndClearSystemIsQuiescentBit() { - bool task_was_run = task_was_run_on_quiescence_monitored_queue_; - task_was_run_on_quiescence_monitored_queue_ = false; - return !task_was_run; -} - -const scoped_refptr<TaskQueueManagerDelegate>& TaskQueueManager::delegate() - const { - return delegate_; -} - -internal::EnqueueOrder TaskQueueManager::GetNextSequenceNumber() { - return enqueue_order_generator_.GenerateNext(); -} - -LazyNow TaskQueueManager::CreateLazyNow() const { - return LazyNow(delegate_.get()); -} - -std::unique_ptr<base::trace_event::ConvertableToTraceFormat> -TaskQueueManager::AsValueWithSelectorResult( - bool should_run, - internal::WorkQueue* selected_work_queue) const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - std::unique_ptr<base::trace_event::TracedValue> state( - new base::trace_event::TracedValue()); - state->BeginArray("queues"); - for (auto& queue : queues_) - queue->AsValueInto(state.get()); - state->EndArray(); - state->BeginDictionary("selector"); - selector_.AsValueInto(state.get()); - state->EndDictionary(); - if (should_run) { - state->SetString("selected_queue", - selected_work_queue->task_queue()->GetName()); - state->SetString("work_queue_name", selected_work_queue->name()); - } - - state->BeginArray("time_domains"); - for (auto& time_domain : time_domains_) - time_domain->AsValueInto(state.get()); - state->EndArray(); - return std::move(state); -} - -void TaskQueueManager::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - // Only schedule DoWork if there's something to do. - if (!queue->immediate_work_queue()->Empty() || - !queue->delayed_work_queue()->Empty()) { - MaybeScheduleImmediateWork(FROM_HERE); - } -} - -void TaskQueueManager::OnTriedToSelectBlockedWorkQueue( - internal::WorkQueue* work_queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK(!work_queue->Empty()); - if (observer_) { - observer_->OnTriedToExecuteBlockedTask(*work_queue->task_queue(), - *work_queue->GetFrontTask()); - } -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/task_queue_manager.h b/chromium/components/scheduler/base/task_queue_manager.h deleted file mode 100644 index 1e9eef4d281..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager.h +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2014 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 CONTENT_RENDERER_SCHEDULER_BASE_TASK_QUEUE_MANAGER_H_ -#define CONTENT_RENDERER_SCHEDULER_BASE_TASK_QUEUE_MANAGER_H_ - -#include <map> - -#include "base/atomic_sequence_num.h" -#include "base/debug/task_annotator.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/pending_task.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_checker.h" -#include "components/scheduler/base/enqueue_order.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_selector.h" -#include "components/scheduler/scheduler_export.h" - -namespace base { -class TickClock; - -namespace trace_event { -class ConvertableToTraceFormat; -class TracedValue; -} // namespace trace_event -} // namespace base - -namespace scheduler { -namespace internal { -class TaskQueueImpl; -} // namespace internal - -class LazyNow; -class RealTimeDomain; -class TimeDomain; -class TaskQueueManagerDelegate; - -// The task queue manager provides N task queues and a selector interface for -// choosing which task queue to service next. Each task queue consists of two -// sub queues: -// -// 1. Incoming task queue. Tasks that are posted get immediately appended here. -// When a task is appended into an empty incoming queue, the task manager -// work function (DoWork) is scheduled to run on the main task runner. -// -// 2. Work queue. If a work queue is empty when DoWork() is entered, tasks from -// the incoming task queue (if any) are moved here. The work queues are -// registered with the selector as input to the scheduling decision. -// -class SCHEDULER_EXPORT TaskQueueManager - : public internal::TaskQueueSelector::Observer { - public: - // Create a task queue manager where |delegate| identifies the thread - // on which where the tasks are eventually run. Category strings must have - // application lifetime (statics or literals). They may not include " chars. - TaskQueueManager(scoped_refptr<TaskQueueManagerDelegate> delegate, - const char* tracing_category, - const char* disabled_by_default_tracing_category, - const char* disabled_by_default_verbose_tracing_category); - ~TaskQueueManager() override; - - // Requests that a task to process work is posted on the main task runner. - // These tasks are de-duplicated in two buckets: main-thread and all other - // threads. This distinction is done to reduce the overehead from locks, we - // assume the main-thread path will be hot. - void MaybeScheduleImmediateWork(const tracked_objects::Location& from_here); - - // Requests that a delayed task to process work is posted on the main task - // runner. These delayed tasks are de-duplicated. Must be called on the thread - // this class was created on. - void MaybeScheduleDelayedWork(const tracked_objects::Location& from_here, - base::TimeTicks now, - base::TimeDelta delay); - - // Set the number of tasks executed in a single invocation of the task queue - // manager. Increasing the batch size can reduce the overhead of yielding - // back to the main message loop -- at the cost of potentially delaying other - // tasks posted to the main loop. The batch size is 1 by default. - void SetWorkBatchSize(int work_batch_size); - - // These functions can only be called on the same thread that the task queue - // manager executes its tasks on. - void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer); - void RemoveTaskObserver(base::MessageLoop::TaskObserver* task_observer); - - // Returns true if any task from a monitored task queue was was run since the - // last call to GetAndClearSystemIsQuiescentBit. - bool GetAndClearSystemIsQuiescentBit(); - - // Creates a task queue with the given |spec|. Must be called on the thread - // this class was created on. - scoped_refptr<internal::TaskQueueImpl> NewTaskQueue( - const TaskQueue::Spec& spec); - - class SCHEDULER_EXPORT Observer { - public: - virtual ~Observer() {} - - // Called when |queue| is unregistered. - virtual void OnUnregisterTaskQueue( - const scoped_refptr<TaskQueue>& queue) = 0; - - // Called when the manager tried to execute a task from a disabled - // queue. See TaskQueue::Spec::SetShouldReportWhenExecutionBlocked. - virtual void OnTriedToExecuteBlockedTask(const TaskQueue& queue, - const base::PendingTask& task) = 0; - }; - - // Called once to set the Observer. This function is called on the main - // thread. If |observer| is null, then no callbacks will occur. - // Note |observer| is expected to outlive the SchedulerHelper. - void SetObserver(Observer* observer); - - // Returns the delegate used by the TaskQueueManager. - const scoped_refptr<TaskQueueManagerDelegate>& delegate() const; - - // Time domains must be registered for the task queues to get updated. - void RegisterTimeDomain(TimeDomain* time_domain); - void UnregisterTimeDomain(TimeDomain* time_domain); - - RealTimeDomain* real_time_domain() const { return real_time_domain_.get(); } - - LazyNow CreateLazyNow() const; - - // Returns the currently executing TaskQueue if any. Must be called on the - // thread this class was created on. - TaskQueue* currently_executing_task_queue() const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - return currently_executing_task_queue_; - } - - private: - friend class LazyNow; - friend class internal::TaskQueueImpl; - friend class TaskQueueManagerTest; - - class DeletionSentinel : public base::RefCounted<DeletionSentinel> { - private: - friend class base::RefCounted<DeletionSentinel>; - ~DeletionSentinel() {} - }; - - // Unregisters a TaskQueue previously created by |NewTaskQueue()|. - // NOTE we have to flush the queue from |newly_updatable_| which means as a - // side effect MoveNewlyUpdatableQueuesIntoUpdatableQueueSet is called by this - // function. - void UnregisterTaskQueue(scoped_refptr<internal::TaskQueueImpl> task_queue); - - // TaskQueueSelector::Observer implementation: - void OnTaskQueueEnabled(internal::TaskQueueImpl* queue) override; - void OnTriedToSelectBlockedWorkQueue( - internal::WorkQueue* work_queue) override; - - // Called by the task queue to register a new pending task. - void DidQueueTask(const internal::TaskQueueImpl::Task& pending_task); - - // Use the selector to choose a pending task and run it. - void DoWork(base::TimeTicks run_time, bool from_main_thread); - - // Delayed Tasks with run_times <= Now() are enqueued onto the work queue. - // Reloads any empty work queues which have automatic pumping enabled and - // which are eligible to be auto pumped based on the |previous_task| which was - // run and |should_trigger_wakeup|. Call with an empty |previous_task| if no - // task was just run. - void UpdateWorkQueues(bool should_trigger_wakeup, - const internal::TaskQueueImpl::Task* previous_task); - - // Chooses the next work queue to service. Returns true if |out_queue| - // indicates the queue from which the next task should be run, false to - // avoid running any tasks. - bool SelectWorkQueueToService(internal::WorkQueue** out_work_queue); - - // Runs a single nestable task from the |queue|. On exit, |out_task| will - // contain the task which was executed. Non-nestable task are reposted on the - // run loop. The queue must not be empty. - enum class ProcessTaskResult { - DEFERRED, - EXECUTED, - TASK_QUEUE_MANAGER_DELETED - }; - ProcessTaskResult ProcessTaskFromWorkQueue( - internal::WorkQueue* work_queue, - internal::TaskQueueImpl::Task* out_previous_task); - - bool RunsTasksOnCurrentThread() const; - bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay); - - internal::EnqueueOrder GetNextSequenceNumber(); - - // Calls MaybeAdvanceTime on all time domains and returns true if one of them - // was able to advance. - bool TryAdvanceTimeDomains(); - - void MaybeRecordTaskDelayHistograms( - const internal::TaskQueueImpl::Task& pending_task, - const internal::TaskQueueImpl* queue); - - std::unique_ptr<base::trace_event::ConvertableToTraceFormat> - AsValueWithSelectorResult(bool should_run, - internal::WorkQueue* selected_work_queue) const; - - std::set<TimeDomain*> time_domains_; - std::unique_ptr<RealTimeDomain> real_time_domain_; - - std::set<scoped_refptr<internal::TaskQueueImpl>> queues_; - - // We have to be careful when deleting a queue because some of the code uses - // raw pointers and doesn't expect the rug to be pulled out from underneath. - std::set<scoped_refptr<internal::TaskQueueImpl>> queues_to_delete_; - - internal::EnqueueOrderGenerator enqueue_order_generator_; - base::debug::TaskAnnotator task_annotator_; - - base::ThreadChecker main_thread_checker_; - scoped_refptr<TaskQueueManagerDelegate> delegate_; - internal::TaskQueueSelector selector_; - - base::Closure from_main_thread_immediate_do_work_closure_; - base::Closure from_other_thread_immediate_do_work_closure_; - - bool task_was_run_on_quiescence_monitored_queue_; - - // To reduce locking overhead we track pending calls to DoWork seperatly for - // the main thread and other threads. - std::set<base::TimeTicks> main_thread_pending_wakeups_; - - // Protects |other_thread_pending_wakeups_|. - mutable base::Lock other_thread_lock_; - std::set<base::TimeTicks> other_thread_pending_wakeups_; - - int work_batch_size_; - size_t task_count_; - - base::ObserverList<base::MessageLoop::TaskObserver> task_observers_; - - const char* tracing_category_; - const char* disabled_by_default_tracing_category_; - const char* disabled_by_default_verbose_tracing_category_; - - internal::TaskQueueImpl* currently_executing_task_queue_; // NOT OWNED - - Observer* observer_; // NOT OWNED - scoped_refptr<DeletionSentinel> deletion_sentinel_; - base::WeakPtrFactory<TaskQueueManager> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(TaskQueueManager); -}; - -} // namespace scheduler - -#endif // CONTENT_RENDERER_SCHEDULER_BASE_TASK_QUEUE_MANAGER_H_ diff --git a/chromium/components/scheduler/base/task_queue_manager_delegate.h b/chromium/components/scheduler/base/task_queue_manager_delegate.h deleted file mode 100644 index 1bb405015f4..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager_delegate.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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_BASE_TASK_QUEUE_MANAGER_DELEGATE_H_ -#define COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/time/tick_clock.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { - -class SCHEDULER_EXPORT TaskQueueManagerDelegate - : public base::SingleThreadTaskRunner, - public base::TickClock { - public: - TaskQueueManagerDelegate() {} - - // Returns true if the task runner is nested (i.e., running a run loop within - // a nested task). - virtual bool IsNested() const = 0; - - protected: - ~TaskQueueManagerDelegate() override {} - - DISALLOW_COPY_AND_ASSIGN(TaskQueueManagerDelegate); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_H_ diff --git a/chromium/components/scheduler/base/task_queue_manager_delegate_for_test.cc b/chromium/components/scheduler/base/task_queue_manager_delegate_for_test.cc deleted file mode 100644 index 56380307db4..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager_delegate_for_test.cc +++ /dev/null @@ -1,56 +0,0 @@ -// 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/base/task_queue_manager_delegate_for_test.h" - -#include <utility> - -#include "base/bind.h" -#include "base/bind_helpers.h" - -namespace scheduler { - -// static -scoped_refptr<TaskQueueManagerDelegateForTest> -TaskQueueManagerDelegateForTest::Create( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - std::unique_ptr<base::TickClock> time_source) { - return make_scoped_refptr( - new TaskQueueManagerDelegateForTest(task_runner, std::move(time_source))); -} - -TaskQueueManagerDelegateForTest::TaskQueueManagerDelegateForTest( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - std::unique_ptr<base::TickClock> time_source) - : task_runner_(task_runner), time_source_(std::move(time_source)) {} - -TaskQueueManagerDelegateForTest::~TaskQueueManagerDelegateForTest() {} - -bool TaskQueueManagerDelegateForTest::PostDelayedTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) { - return task_runner_->PostDelayedTask(from_here, task, delay); -} - -bool TaskQueueManagerDelegateForTest::PostNonNestableDelayedTask( - const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) { - return task_runner_->PostNonNestableDelayedTask(from_here, task, delay); -} - -bool TaskQueueManagerDelegateForTest::RunsTasksOnCurrentThread() const { - return task_runner_->RunsTasksOnCurrentThread(); -} - -bool TaskQueueManagerDelegateForTest::IsNested() const { - return false; -} - -base::TimeTicks TaskQueueManagerDelegateForTest::NowTicks() { - return time_source_->NowTicks(); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/task_queue_manager_delegate_for_test.h b/chromium/components/scheduler/base/task_queue_manager_delegate_for_test.h deleted file mode 100644 index cb97e474e7f..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager_delegate_for_test.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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_BASE_TASK_QUEUE_MANAGER_DELEGATE_FOR_TEST_H_ -#define COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_FOR_TEST_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/time/tick_clock.h" -#include "components/scheduler/base/task_queue_manager_delegate.h" - -namespace scheduler { - -class TaskQueueManagerDelegateForTest : public TaskQueueManagerDelegate { - public: - static scoped_refptr<TaskQueueManagerDelegateForTest> Create( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - std::unique_ptr<base::TickClock> time_source); - - // NestableSingleThreadTaskRunner implementation - bool PostDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) override; - bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, - const base::Closure& task, - base::TimeDelta delay) override; - bool RunsTasksOnCurrentThread() const override; - bool IsNested() const override; - base::TimeTicks NowTicks() override; - - protected: - ~TaskQueueManagerDelegateForTest() override; - TaskQueueManagerDelegateForTest( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - std::unique_ptr<base::TickClock> time_source); - - private: - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - std::unique_ptr<base::TickClock> time_source_; - - DISALLOW_COPY_AND_ASSIGN(TaskQueueManagerDelegateForTest); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_FOR_TEST_H_ diff --git a/chromium/components/scheduler/base/task_queue_manager_perftest.cc b/chromium/components/scheduler/base/task_queue_manager_perftest.cc deleted file mode 100644 index a81fea329dc..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager_perftest.cc +++ /dev/null @@ -1,163 +0,0 @@ -// 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/base/task_queue_manager.h" - -#include <stddef.h> - -#include "base/bind.h" -#include "base/memory/ptr_util.h" -#include "base/threading/thread.h" -#include "base/time/default_tick_clock.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager_delegate_for_test.h" -#include "components/scheduler/base/task_queue_selector.h" -#include "components/scheduler/base/work_queue_sets.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" - -namespace scheduler { - -class TaskQueueManagerPerfTest : public testing::Test { - public: - TaskQueueManagerPerfTest() - : num_queues_(0), - max_tasks_in_flight_(0), - num_tasks_in_flight_(0), - num_tasks_to_post_(0), - num_tasks_to_run_(0) {} - - void SetUp() override { - if (base::ThreadTicks::IsSupported()) - base::ThreadTicks::WaitUntilInitialized(); - } - - void Initialize(size_t num_queues) { - num_queues_ = num_queues; - message_loop_.reset(new base::MessageLoop()); - manager_ = base::WrapUnique(new TaskQueueManager( - TaskQueueManagerDelegateForTest::Create( - message_loop_->task_runner(), - base::WrapUnique(new base::DefaultTickClock())), - "fake.category", "fake.category", "fake.category.debug")); - for (size_t i = 0; i < num_queues; i++) - queues_.push_back(manager_->NewTaskQueue(TaskQueue::Spec("test"))); - } - - void TestDelayedTask() { - if (--num_tasks_to_run_ == 0) { - message_loop_->QuitWhenIdle(); - } - - num_tasks_in_flight_--; - // NOTE there are only up to max_tasks_in_flight_ pending delayed tasks at - // any one time. Thanks to the lower_num_tasks_to_post going to zero if - // there are a lot of tasks in flight, the total number of task in flight at - // any one time is very variable. - unsigned int lower_num_tasks_to_post = - num_tasks_in_flight_ < (max_tasks_in_flight_ / 2) ? 1 : 0; - unsigned int max_tasks_to_post = - num_tasks_to_post_ % 2 ? lower_num_tasks_to_post : 10; - for (unsigned int i = 0; - i < max_tasks_to_post && num_tasks_in_flight_ < max_tasks_in_flight_ && - num_tasks_to_post_ > 0; - i++) { - // Choose a queue weighted towards queue 0. - unsigned int queue = num_tasks_to_post_ % (num_queues_ + 1); - if (queue == num_queues_) { - queue = 0; - } - // Simulate a mix of short and longer delays. - unsigned int delay = - num_tasks_to_post_ % 2 ? 1 : (10 + num_tasks_to_post_ % 10); - queues_[queue]->PostDelayedTask( - FROM_HERE, base::Bind(&TaskQueueManagerPerfTest::TestDelayedTask, - base::Unretained(this)), - base::TimeDelta::FromMicroseconds(delay)); - num_tasks_in_flight_++; - num_tasks_to_post_--; - } - } - - void ResetAndCallTestDelayedTask(unsigned int num_tasks_to_run) { - num_tasks_in_flight_ = 1; - num_tasks_to_post_ = num_tasks_to_run; - num_tasks_to_run_ = num_tasks_to_run; - TestDelayedTask(); - } - - void Benchmark(const std::string& trace, const base::Closure& test_task) { - base::ThreadTicks start = base::ThreadTicks::Now(); - base::ThreadTicks now; - unsigned long long num_iterations = 0; - do { - test_task.Run(); - message_loop_->Run(); - now = base::ThreadTicks::Now(); - num_iterations++; - } while (now - start < base::TimeDelta::FromSeconds(5)); - perf_test::PrintResult( - "task", "", trace, - (now - start).InMicroseconds() / static_cast<double>(num_iterations), - "us/run", true); - } - - size_t num_queues_; - unsigned int max_tasks_in_flight_; - unsigned int num_tasks_in_flight_; - unsigned int num_tasks_to_post_; - unsigned int num_tasks_to_run_; - std::unique_ptr<TaskQueueManager> manager_; - std::unique_ptr<base::MessageLoop> message_loop_; - std::vector<scoped_refptr<base::SingleThreadTaskRunner>> queues_; -}; - -TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_OneQueue) { - if (!base::ThreadTicks::IsSupported()) - return; - Initialize(1u); - - max_tasks_in_flight_ = 200; - Benchmark("run 10000 delayed tasks with one queue", - base::Bind(&TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask, - base::Unretained(this), 10000)); -} - -TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_FourQueues) { - if (!base::ThreadTicks::IsSupported()) - return; - Initialize(4u); - - max_tasks_in_flight_ = 200; - Benchmark("run 10000 delayed tasks with four queues", - base::Bind(&TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask, - base::Unretained(this), 10000)); -} - -TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_EightQueues) { - if (!base::ThreadTicks::IsSupported()) - return; - Initialize(8u); - - max_tasks_in_flight_ = 200; - Benchmark("run 10000 delayed tasks with eight queues", - base::Bind(&TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask, - base::Unretained(this), 10000)); -} - -TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_ThirtyTwoQueues) { - if (!base::ThreadTicks::IsSupported()) - return; - Initialize(32u); - - max_tasks_in_flight_ = 200; - Benchmark("run 10000 delayed tasks with eight queues", - base::Bind(&TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask, - base::Unretained(this), 10000)); -} - -// TODO(alexclarke): Add additional tests with different mixes of non-delayed vs -// delayed tasks. - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/task_queue_manager_unittest.cc b/chromium/components/scheduler/base/task_queue_manager_unittest.cc deleted file mode 100644 index fef14d2fe57..00000000000 --- a/chromium/components/scheduler/base/task_queue_manager_unittest.cc +++ /dev/null @@ -1,1929 +0,0 @@ -// Copyright 2014 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/base/task_queue_manager.h" - -#include <stddef.h> - -#include <utility> - -#include "base/location.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted_memory.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/test/simple_test_tick_clock.h" -#include "base/test/trace_event_analyzer.h" -#include "base/threading/thread.h" -#include "base/trace_event/blame_context.h" -#include "base/trace_event/trace_buffer.h" -#include "cc/test/ordered_simple_task_runner.h" -#include "components/scheduler/base/real_time_domain.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager_delegate_for_test.h" -#include "components/scheduler/base/task_queue_selector.h" -#include "components/scheduler/base/test_always_fail_time_source.h" -#include "components/scheduler/base/test_time_source.h" -#include "components/scheduler/base/virtual_time_domain.h" -#include "components/scheduler/base/work_queue.h" -#include "components/scheduler/base/work_queue_sets.h" -#include "testing/gmock/include/gmock/gmock.h" - -using testing::ElementsAre; -using testing::ElementsAreArray; -using testing::_; -using scheduler::internal::EnqueueOrder; - -namespace scheduler { - -class MessageLoopTaskRunner : public TaskQueueManagerDelegateForTest { - public: - static scoped_refptr<MessageLoopTaskRunner> Create( - std::unique_ptr<base::TickClock> tick_clock) { - return make_scoped_refptr(new MessageLoopTaskRunner(std::move(tick_clock))); - } - - // NestableTaskRunner implementation. - bool IsNested() const override { - return base::MessageLoop::current()->IsNested(); - } - - private: - explicit MessageLoopTaskRunner(std::unique_ptr<base::TickClock> tick_clock) - : TaskQueueManagerDelegateForTest( - base::MessageLoop::current()->task_runner(), - std::move(tick_clock)) {} - ~MessageLoopTaskRunner() override {} -}; - -class TaskQueueManagerTest : public testing::Test { - public: - void DeleteTaskQueueManager() { manager_.reset(); } - - protected: - void InitializeWithClock(size_t num_queues, - std::unique_ptr<base::TickClock> test_time_source) { - test_task_runner_ = make_scoped_refptr( - new cc::OrderedSimpleTaskRunner(now_src_.get(), false)); - main_task_runner_ = TaskQueueManagerDelegateForTest::Create( - test_task_runner_.get(), - base::WrapUnique(new TestTimeSource(now_src_.get()))); - manager_ = base::WrapUnique( - new TaskQueueManager(main_task_runner_, "test.scheduler", - "test.scheduler", "test.scheduler.debug")); - - for (size_t i = 0; i < num_queues; i++) - runners_.push_back(manager_->NewTaskQueue(TaskQueue::Spec("test_queue"))); - } - - void Initialize(size_t num_queues) { - now_src_.reset(new base::SimpleTestTickClock()); - now_src_->Advance(base::TimeDelta::FromMicroseconds(1000)); - InitializeWithClock(num_queues, - base::WrapUnique(new TestTimeSource(now_src_.get()))); - } - - void InitializeWithRealMessageLoop(size_t num_queues) { - now_src_.reset(new base::SimpleTestTickClock()); - message_loop_.reset(new base::MessageLoop()); - manager_ = base::WrapUnique(new TaskQueueManager( - MessageLoopTaskRunner::Create( - base::WrapUnique(new TestTimeSource(now_src_.get()))), - "test.scheduler", "test.scheduler", "test.scheduler.debug")); - - for (size_t i = 0; i < num_queues; i++) - runners_.push_back(manager_->NewTaskQueue(TaskQueue::Spec("test_queue"))); - } - - std::unique_ptr<base::MessageLoop> message_loop_; - std::unique_ptr<base::SimpleTestTickClock> now_src_; - scoped_refptr<TaskQueueManagerDelegateForTest> main_task_runner_; - scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner_; - std::unique_ptr<TaskQueueManager> manager_; - std::vector<scoped_refptr<internal::TaskQueueImpl>> runners_; -}; - -void PostFromNestedRunloop(base::MessageLoop* message_loop, - base::SingleThreadTaskRunner* runner, - std::vector<std::pair<base::Closure, bool>>* tasks) { - base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); - for (std::pair<base::Closure, bool>& pair : *tasks) { - if (pair.second) { - runner->PostTask(FROM_HERE, pair.first); - } else { - runner->PostNonNestableTask(FROM_HERE, pair.first); - } - } - base::RunLoop().RunUntilIdle(); -} - -void NopTask() {} - -TEST_F(TaskQueueManagerTest, NowNotCalledWhenThereAreNoDelayedTasks) { - message_loop_.reset(new base::MessageLoop()); - manager_ = base::WrapUnique(new TaskQueueManager( - MessageLoopTaskRunner::Create( - base::WrapUnique(new TestAlwaysFailTimeSource())), - "test.scheduler", "test.scheduler", "test.scheduler.debug")); - - for (size_t i = 0; i < 3; i++) - runners_.push_back(manager_->NewTaskQueue(TaskQueue::Spec("test_queue"))); - - runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&NopTask)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&NopTask)); - runners_[2]->PostTask(FROM_HERE, base::Bind(&NopTask)); - runners_[2]->PostTask(FROM_HERE, base::Bind(&NopTask)); - - message_loop_->RunUntilIdle(); -} - -void NullTask() {} - -void TestTask(EnqueueOrder value, std::vector<EnqueueOrder>* out_result) { - out_result->push_back(value); -} - -TEST_F(TaskQueueManagerTest, SingleQueuePosting) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); -} - -TEST_F(TaskQueueManagerTest, MultiQueuePosting) { - Initialize(3u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); - runners_[2]->PostTask(FROM_HERE, base::Bind(&TestTask, 5, &run_order)); - runners_[2]->PostTask(FROM_HERE, base::Bind(&TestTask, 6, &run_order)); - - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6)); -} - -TEST_F(TaskQueueManagerTest, NonNestableTaskPosting) { - InitializeWithRealMessageLoop(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostNonNestableTask(FROM_HERE, - base::Bind(&TestTask, 1, &run_order)); - - message_loop_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, NonNestableTaskExecutesInExpectedOrder) { - InitializeWithRealMessageLoop(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); - runners_[0]->PostNonNestableTask(FROM_HERE, - base::Bind(&TestTask, 5, &run_order)); - - message_loop_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5)); -} - -TEST_F(TaskQueueManagerTest, NonNestableTaskDoesntExecuteInNestedLoop) { - InitializeWithRealMessageLoop(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - - std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&TestTask, 3, &run_order), false)); - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&TestTask, 4, &run_order), true)); - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&TestTask, 5, &run_order), true)); - - runners_[0]->PostTask( - FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), - base::RetainedRef(runners_[0]), - base::Unretained(&tasks_to_post_from_nested_loop))); - - message_loop_->RunUntilIdle(); - // Note we expect task 3 to run last because it's non-nestable. - EXPECT_THAT(run_order, ElementsAre(1, 2, 4, 5, 3)); -} - -TEST_F(TaskQueueManagerTest, QueuePolling) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - EXPECT_FALSE(runners_[0]->HasPendingImmediateWork()); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - EXPECT_TRUE(runners_[0]->HasPendingImmediateWork()); - - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(runners_[0]->HasPendingImmediateWork()); -} - -TEST_F(TaskQueueManagerTest, DelayedTaskPosting) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay); - EXPECT_EQ(delay, test_task_runner_->DelayToNextTaskTime()); - EXPECT_FALSE(runners_[0]->HasPendingImmediateWork()); - EXPECT_TRUE(run_order.empty()); - - // The task doesn't run before the delay has completed. - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(9)); - EXPECT_TRUE(run_order.empty()); - - // After the delay has completed, the task runs normally. - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); - EXPECT_THAT(run_order, ElementsAre(1)); - EXPECT_FALSE(runners_[0]->HasPendingImmediateWork()); -} - -bool MessageLoopTaskCounter(size_t* count) { - *count = *count + 1; - return true; -} - -TEST_F(TaskQueueManagerTest, DelayedTaskExecutedInOneMessageLoopTask) { - Initialize(1u); - - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay); - - size_t task_count = 0; - test_task_runner_->RunTasksWhile( - base::Bind(&MessageLoopTaskCounter, &task_count)); - EXPECT_EQ(1u, task_count); -} - -TEST_F(TaskQueueManagerTest, DelayedTaskPosting_MultipleTasks_DecendingOrder) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - base::TimeDelta::FromMilliseconds(10)); - - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - base::TimeDelta::FromMilliseconds(8)); - - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - base::TimeDelta::FromMilliseconds(5)); - - EXPECT_EQ(base::TimeDelta::FromMilliseconds(5), - test_task_runner_->DelayToNextTaskTime()); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5)); - EXPECT_THAT(run_order, ElementsAre(3)); - EXPECT_EQ(base::TimeDelta::FromMilliseconds(3), - test_task_runner_->DelayToNextTaskTime()); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(3)); - EXPECT_THAT(run_order, ElementsAre(3, 2)); - EXPECT_EQ(base::TimeDelta::FromMilliseconds(2), - test_task_runner_->DelayToNextTaskTime()); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(2)); - EXPECT_THAT(run_order, ElementsAre(3, 2, 1)); -} - -TEST_F(TaskQueueManagerTest, DelayedTaskPosting_MultipleTasks_AscendingOrder) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - base::TimeDelta::FromMilliseconds(1)); - - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - base::TimeDelta::FromMilliseconds(5)); - - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - base::TimeDelta::FromMilliseconds(10)); - - EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), - test_task_runner_->DelayToNextTaskTime()); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); - EXPECT_THAT(run_order, ElementsAre(1)); - EXPECT_EQ(base::TimeDelta::FromMilliseconds(4), - test_task_runner_->DelayToNextTaskTime()); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(4)); - EXPECT_THAT(run_order, ElementsAre(1, 2)); - EXPECT_EQ(base::TimeDelta::FromMilliseconds(5), - test_task_runner_->DelayToNextTaskTime()); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5)); - EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); -} - -TEST_F(TaskQueueManagerTest, PostDelayedTask_SharesUnderlyingDelayedTasks) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - delay); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - delay); - - EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); -} - -class TestObject { - public: - ~TestObject() { destructor_count_++; } - - void Run() { FAIL() << "TestObject::Run should not be called"; } - - static int destructor_count_; -}; - -int TestObject::destructor_count_ = 0; - -TEST_F(TaskQueueManagerTest, PendingDelayedTasksRemovedOnShutdown) { - Initialize(1u); - - TestObject::destructor_count_ = 0; - - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask( - FROM_HERE, base::Bind(&TestObject::Run, base::Owned(new TestObject())), - delay); - runners_[0]->PostTask( - FROM_HERE, base::Bind(&TestObject::Run, base::Owned(new TestObject()))); - - manager_.reset(); - - EXPECT_EQ(2, TestObject::destructor_count_); -} - -TEST_F(TaskQueueManagerTest, ManualPumping) { - Initialize(1u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - // Posting a task when pumping is disabled doesn't result in work getting - // posted. - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - EXPECT_FALSE(test_task_runner_->HasPendingTasks()); - - // However polling still works. - EXPECT_TRUE(runners_[0]->HasPendingImmediateWork()); - - // After pumping the task runs normally. - LazyNow lazy_now(now_src_.get()); - runners_[0]->PumpQueue(&lazy_now, true); - EXPECT_TRUE(test_task_runner_->HasPendingTasks()); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, ManualPumpingToggle) { - Initialize(1u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - // Posting a task when pumping is disabled doesn't result in work getting - // posted. - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - EXPECT_FALSE(test_task_runner_->HasPendingTasks()); - - // When pumping is enabled the task runs normally. - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AUTO); - EXPECT_TRUE(test_task_runner_->HasPendingTasks()); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, DenyRunning_BeforePosting) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->SetQueueEnabled(false); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); - - runners_[0]->SetQueueEnabled(true); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, DenyRunning_AfterPosting) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->SetQueueEnabled(false); - - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); - - runners_[0]->SetQueueEnabled(true); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, DenyRunning_ManuallyPumpedTransitionsToAuto) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - runners_[0]->SetQueueEnabled(false); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); - - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AUTO); - runners_[0]->SetQueueEnabled(true); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, ManualPumpingWithDelayedTask) { - Initialize(1u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - // Posting a delayed task when pumping will apply the delay, but won't cause - // work to executed afterwards. - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay); - - // After pumping but before the delay period has expired, task does not run. - LazyNow lazy_now1(now_src_.get()); - runners_[0]->PumpQueue(&lazy_now1, true); - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5)); - EXPECT_TRUE(run_order.empty()); - - // Once the delay has expired, pumping causes the task to run. - now_src_->Advance(base::TimeDelta::FromMilliseconds(5)); - LazyNow lazy_now2(now_src_.get()); - runners_[0]->PumpQueue(&lazy_now2, true); - EXPECT_TRUE(test_task_runner_->HasPendingTasks()); - test_task_runner_->RunPendingTasks(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -TEST_F(TaskQueueManagerTest, ManualPumpingWithMultipleDelayedTasks) { - Initialize(1u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - // Posting a delayed task when pumping will apply the delay, but won't cause - // work to executed afterwards. - base::TimeDelta delay1(base::TimeDelta::FromMilliseconds(1)); - base::TimeDelta delay2(base::TimeDelta::FromMilliseconds(10)); - base::TimeDelta delay3(base::TimeDelta::FromMilliseconds(20)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay1); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - delay2); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - delay3); - - now_src_->Advance(base::TimeDelta::FromMilliseconds(15)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); - - // Once the delay has expired, pumping causes the task to run. - LazyNow lazy_now(now_src_.get()); - runners_[0]->PumpQueue(&lazy_now, true); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1, 2)); -} - -TEST_F(TaskQueueManagerTest, DelayedTasksDontAutoRunWithManualPumping) { - Initialize(1u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(10)); - EXPECT_TRUE(run_order.empty()); -} - -TEST_F(TaskQueueManagerTest, ManualPumpingWithNonEmptyWorkQueue) { - Initialize(1u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - // Posting two tasks and pumping twice should result in two tasks in the work - // queue. - LazyNow lazy_now(now_src_.get()); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PumpQueue(&lazy_now, true); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[0]->PumpQueue(&lazy_now, true); - - EXPECT_EQ(2u, runners_[0]->immediate_work_queue()->Size()); -} - -void ReentrantTestTask(scoped_refptr<base::SingleThreadTaskRunner> runner, - int countdown, - std::vector<EnqueueOrder>* out_result) { - out_result->push_back(countdown); - if (--countdown) { - runner->PostTask(FROM_HERE, - Bind(&ReentrantTestTask, runner, countdown, out_result)); - } -} - -TEST_F(TaskQueueManagerTest, ReentrantPosting) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, - Bind(&ReentrantTestTask, runners_[0], 3, &run_order)); - - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(3, 2, 1)); -} - -TEST_F(TaskQueueManagerTest, NoTasksAfterShutdown) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - manager_.reset(); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); -} - -void PostTaskToRunner(scoped_refptr<base::SingleThreadTaskRunner> runner, - std::vector<EnqueueOrder>* run_order) { - runner->PostTask(FROM_HERE, base::Bind(&TestTask, 1, run_order)); -} - -TEST_F(TaskQueueManagerTest, PostFromThread) { - InitializeWithRealMessageLoop(1u); - - std::vector<EnqueueOrder> run_order; - base::Thread thread("TestThread"); - thread.Start(); - thread.task_runner()->PostTask( - FROM_HERE, base::Bind(&PostTaskToRunner, runners_[0], &run_order)); - thread.Stop(); - - message_loop_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1)); -} - -void RePostingTestTask(scoped_refptr<base::SingleThreadTaskRunner> runner, - int* run_count) { - (*run_count)++; - runner->PostTask(FROM_HERE, Bind(&RePostingTestTask, - base::Unretained(runner.get()), run_count)); -} - -TEST_F(TaskQueueManagerTest, DoWorkCantPostItselfMultipleTimes) { - Initialize(1u); - - int run_count = 0; - runners_[0]->PostTask( - FROM_HERE, base::Bind(&RePostingTestTask, runners_[0], &run_count)); - - test_task_runner_->RunPendingTasks(); - // NOTE without the executing_task_ check in MaybeScheduleDoWork there - // will be two tasks here. - EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); - EXPECT_EQ(1, run_count); -} - -TEST_F(TaskQueueManagerTest, PostFromNestedRunloop) { - InitializeWithRealMessageLoop(1u); - - std::vector<EnqueueOrder> run_order; - std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&TestTask, 1, &run_order), true)); - - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 0, &run_order)); - runners_[0]->PostTask( - FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), - base::RetainedRef(runners_[0]), - base::Unretained(&tasks_to_post_from_nested_loop))); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - - message_loop_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(0, 2, 1)); -} - -TEST_F(TaskQueueManagerTest, WorkBatching) { - Initialize(1u); - - manager_->SetWorkBatchSize(2); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); - - // Running one task in the host message loop should cause two posted tasks to - // get executed. - EXPECT_EQ(test_task_runner_->NumPendingTasks(), 1u); - test_task_runner_->RunPendingTasks(); - EXPECT_THAT(run_order, ElementsAre(1, 2)); - - // The second task runs the remaining two posted tasks. - EXPECT_EQ(test_task_runner_->NumPendingTasks(), 1u); - test_task_runner_->RunPendingTasks(); - EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); -} - -TEST_F(TaskQueueManagerTest, AutoPumpAfterWakeup) { - Initialize(2u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); // Shouldn't run - no other task to wake TQM. - - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); // Still shouldn't wake TQM. - - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - test_task_runner_->RunUntilIdle(); - // Executing a task on an auto pumped queue should wake the TQM. - EXPECT_THAT(run_order, ElementsAre(3, 1, 2)); -} - -TEST_F(TaskQueueManagerTest, AutoPumpAfterWakeupWhenAlreadyAwake) { - Initialize(2u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(2, 1)); // TQM was already awake. -} - -TEST_F(TaskQueueManagerTest, - AutoPumpAfterWakeupTriggeredByManuallyPumpedQueue) { - Initialize(2u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - runners_[1]->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); // Shouldn't run - no other task to wake TQM. - - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - test_task_runner_->RunUntilIdle(); - // This still shouldn't wake TQM as manual queue was not pumped. - EXPECT_TRUE(run_order.empty()); - - LazyNow lazy_now(now_src_.get()); - runners_[1]->PumpQueue(&lazy_now, true); - test_task_runner_->RunUntilIdle(); - // Executing a task on an auto pumped queue should wake the TQM. - EXPECT_THAT(run_order, ElementsAre(2, 1)); -} - -void TestPostingTask(scoped_refptr<base::SingleThreadTaskRunner> task_runner, - base::Closure task) { - task_runner->PostTask(FROM_HERE, task); -} - -TEST_F(TaskQueueManagerTest, AutoPumpAfterWakeupFromTask) { - Initialize(2u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - - std::vector<EnqueueOrder> run_order; - // Check that a task which posts a task to an auto pump after wakeup queue - // doesn't cause the queue to wake up. - base::Closure after_wakeup_task = base::Bind(&TestTask, 1, &run_order); - runners_[1]->PostTask( - FROM_HERE, base::Bind(&TestPostingTask, runners_[0], after_wakeup_task)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); - - // Wake up the queue. - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(2, 1)); -} - -TEST_F(TaskQueueManagerTest, AutoPumpAfterWakeupFromMultipleTasks) { - Initialize(2u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - - std::vector<EnqueueOrder> run_order; - // Check that a task which posts a task to an auto pump after wakeup queue - // doesn't cause the queue to wake up. - base::Closure after_wakeup_task_1 = base::Bind(&TestTask, 1, &run_order); - base::Closure after_wakeup_task_2 = base::Bind(&TestTask, 2, &run_order); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestPostingTask, runners_[0], - after_wakeup_task_1)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestPostingTask, runners_[0], - after_wakeup_task_2)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(run_order.empty()); - - // Wake up the queue. - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(3, 1, 2)); -} - -TEST_F(TaskQueueManagerTest, AutoPumpAfterWakeupBecomesQuiescent) { - Initialize(2u); - runners_[0]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - - int run_count = 0; - // Check that if multiple tasks reposts themselves onto a pump-after-wakeup - // queue they don't wake each other and will eventually stop when no other - // tasks execute. - runners_[0]->PostTask( - FROM_HERE, base::Bind(&RePostingTestTask, runners_[0], &run_count)); - runners_[0]->PostTask( - FROM_HERE, base::Bind(&RePostingTestTask, runners_[0], &run_count)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&NopTask)); - test_task_runner_->RunUntilIdle(); - // The reposting tasks posted to the after wakeup queue shouldn't have woken - // each other up. - EXPECT_EQ(2, run_count); -} - -TEST_F(TaskQueueManagerTest, AutoPumpAfterWakeupWithDontWakeQueue) { - Initialize(1u); - - scoped_refptr<internal::TaskQueueImpl> queue0 = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue 0") - .SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP)); - scoped_refptr<internal::TaskQueueImpl> queue1 = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue 0") - .SetWakeupPolicy(TaskQueue::WakeupPolicy::DONT_WAKE_OTHER_QUEUES)); - scoped_refptr<internal::TaskQueueImpl> queue2 = runners_[0]; - - std::vector<EnqueueOrder> run_order; - queue0->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - queue1->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - test_task_runner_->RunUntilIdle(); - // Executing a DONT_WAKE_OTHER_QUEUES queue shouldn't wake the autopump after - // wakeup queue. - EXPECT_THAT(run_order, ElementsAre(2)); - - queue2->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - test_task_runner_->RunUntilIdle(); - // Executing a CAN_WAKE_OTHER_QUEUES queue should wake the autopump after - // wakeup queue. - EXPECT_THAT(run_order, ElementsAre(2, 3, 1)); -} - -class MockTaskObserver : public base::MessageLoop::TaskObserver { - public: - MOCK_METHOD1(DidProcessTask, void(const base::PendingTask& task)); - MOCK_METHOD1(WillProcessTask, void(const base::PendingTask& task)); -}; - -TEST_F(TaskQueueManagerTest, TaskObserverAdding) { - InitializeWithRealMessageLoop(1u); - MockTaskObserver observer; - - manager_->SetWorkBatchSize(2); - manager_->AddTaskObserver(&observer); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - - EXPECT_CALL(observer, WillProcessTask(_)).Times(2); - EXPECT_CALL(observer, DidProcessTask(_)).Times(2); - message_loop_->RunUntilIdle(); -} - -TEST_F(TaskQueueManagerTest, TaskObserverRemoving) { - InitializeWithRealMessageLoop(1u); - MockTaskObserver observer; - manager_->SetWorkBatchSize(2); - manager_->AddTaskObserver(&observer); - manager_->RemoveTaskObserver(&observer); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - - EXPECT_CALL(observer, WillProcessTask(_)).Times(0); - EXPECT_CALL(observer, DidProcessTask(_)).Times(0); - - message_loop_->RunUntilIdle(); -} - -void RemoveObserverTask(TaskQueueManager* manager, - base::MessageLoop::TaskObserver* observer) { - manager->RemoveTaskObserver(observer); -} - -TEST_F(TaskQueueManagerTest, TaskObserverRemovingInsideTask) { - InitializeWithRealMessageLoop(1u); - MockTaskObserver observer; - manager_->SetWorkBatchSize(3); - manager_->AddTaskObserver(&observer); - - runners_[0]->PostTask( - FROM_HERE, base::Bind(&RemoveObserverTask, manager_.get(), &observer)); - - EXPECT_CALL(observer, WillProcessTask(_)).Times(1); - EXPECT_CALL(observer, DidProcessTask(_)).Times(0); - message_loop_->RunUntilIdle(); -} - -TEST_F(TaskQueueManagerTest, QueueTaskObserverAdding) { - InitializeWithRealMessageLoop(2u); - MockTaskObserver observer; - - manager_->SetWorkBatchSize(2); - runners_[0]->AddTaskObserver(&observer); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - - EXPECT_CALL(observer, WillProcessTask(_)).Times(1); - EXPECT_CALL(observer, DidProcessTask(_)).Times(1); - message_loop_->RunUntilIdle(); -} - -TEST_F(TaskQueueManagerTest, QueueTaskObserverRemoving) { - InitializeWithRealMessageLoop(1u); - MockTaskObserver observer; - manager_->SetWorkBatchSize(2); - runners_[0]->AddTaskObserver(&observer); - runners_[0]->RemoveTaskObserver(&observer); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - - EXPECT_CALL(observer, WillProcessTask(_)).Times(0); - EXPECT_CALL(observer, DidProcessTask(_)).Times(0); - - message_loop_->RunUntilIdle(); -} - -void RemoveQueueObserverTask(scoped_refptr<TaskQueue> queue, - base::MessageLoop::TaskObserver* observer) { - queue->RemoveTaskObserver(observer); -} - -TEST_F(TaskQueueManagerTest, QueueTaskObserverRemovingInsideTask) { - InitializeWithRealMessageLoop(1u); - MockTaskObserver observer; - runners_[0]->AddTaskObserver(&observer); - - runners_[0]->PostTask( - FROM_HERE, base::Bind(&RemoveQueueObserverTask, runners_[0], &observer)); - - EXPECT_CALL(observer, WillProcessTask(_)).Times(1); - EXPECT_CALL(observer, DidProcessTask(_)).Times(0); - message_loop_->RunUntilIdle(); -} - -TEST_F(TaskQueueManagerTest, ThreadCheckAfterTermination) { - Initialize(1u); - EXPECT_TRUE(runners_[0]->RunsTasksOnCurrentThread()); - manager_.reset(); - EXPECT_TRUE(runners_[0]->RunsTasksOnCurrentThread()); -} - -TEST_F(TaskQueueManagerTest, TimeDomain_NextScheduledRunTime) { - Initialize(2u); - now_src_->Advance(base::TimeDelta::FromMicroseconds(10000)); - - // With no delayed tasks. - base::TimeTicks run_time; - EXPECT_FALSE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - - // With a non-delayed task. - runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); - EXPECT_FALSE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - - // With a delayed task. - base::TimeDelta expected_delay = base::TimeDelta::FromMilliseconds(50); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), expected_delay); - EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); - - // With another delayed task in the same queue with a longer delay. - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), - base::TimeDelta::FromMilliseconds(100)); - EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); - - // With another delayed task in the same queue with a shorter delay. - expected_delay = base::TimeDelta::FromMilliseconds(20); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), expected_delay); - EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); - - // With another delayed task in a different queue with a shorter delay. - expected_delay = base::TimeDelta::FromMilliseconds(10); - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), expected_delay); - EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); - - // Test it updates as time progresses - now_src_->Advance(expected_delay); - EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - EXPECT_EQ(now_src_->NowTicks(), run_time); -} - -TEST_F(TaskQueueManagerTest, TimeDomain_NextScheduledRunTime_MultipleQueues) { - Initialize(3u); - - base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(50); - base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(5); - base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(10); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1); - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay2); - runners_[2]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay3); - runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); - - base::TimeTicks run_time; - EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); - EXPECT_EQ(now_src_->NowTicks() + delay2, run_time); -} - -TEST_F(TaskQueueManagerTest, DeleteTaskQueueManagerInsideATask) { - Initialize(1u); - - runners_[0]->PostTask( - FROM_HERE, base::Bind(&TaskQueueManagerTest::DeleteTaskQueueManager, - base::Unretained(this))); - - // This should not crash, assuming DoWork detects the TaskQueueManager has - // been deleted. - test_task_runner_->RunUntilIdle(); -} - -TEST_F(TaskQueueManagerTest, GetAndClearSystemIsQuiescentBit) { - Initialize(3u); - - scoped_refptr<internal::TaskQueueImpl> queue0 = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue 0").SetShouldMonitorQuiescence(true)); - scoped_refptr<internal::TaskQueueImpl> queue1 = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue 1").SetShouldMonitorQuiescence(true)); - scoped_refptr<internal::TaskQueueImpl> queue2 = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue 2").SetShouldMonitorQuiescence(false)); - - EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); - - queue0->PostTask(FROM_HERE, base::Bind(&NopTask)); - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit()); - EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); - - queue1->PostTask(FROM_HERE, base::Bind(&NopTask)); - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit()); - EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); - - queue2->PostTask(FROM_HERE, base::Bind(&NopTask)); - test_task_runner_->RunUntilIdle(); - EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); - - queue0->PostTask(FROM_HERE, base::Bind(&NopTask)); - queue1->PostTask(FROM_HERE, base::Bind(&NopTask)); - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit()); - EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); -} - -TEST_F(TaskQueueManagerTest, HasPendingImmediateWork) { - 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_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue1->HasPendingImmediateWork()); - - queue0->PostTask(FROM_HERE, base::Bind(NullTask)); - queue1->PostTask(FROM_HERE, base::Bind(NullTask)); - EXPECT_TRUE(queue0->HasPendingImmediateWork()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - - LazyNow lazy_now(now_src_.get()); - queue1->PumpQueue(&lazy_now, true); - EXPECT_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue1->HasPendingImmediateWork()); -} - -TEST_F(TaskQueueManagerTest, HasPendingImmediateWorkAndNeedsPumping) { - 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_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue0->NeedsPumping()); - EXPECT_FALSE(queue1->HasPendingImmediateWork()); - EXPECT_FALSE(queue1->NeedsPumping()); - - queue0->PostTask(FROM_HERE, base::Bind(NullTask)); - queue0->PostTask(FROM_HERE, base::Bind(NullTask)); - queue1->PostTask(FROM_HERE, base::Bind(NullTask)); - EXPECT_TRUE(queue0->HasPendingImmediateWork()); - EXPECT_TRUE(queue0->NeedsPumping()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - EXPECT_TRUE(queue1->NeedsPumping()); - - test_task_runner_->SetRunTaskLimit(1); - test_task_runner_->RunPendingTasks(); - EXPECT_TRUE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue0->NeedsPumping()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - EXPECT_TRUE(queue1->NeedsPumping()); - - test_task_runner_->ClearRunTaskLimit(); - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue0->NeedsPumping()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - EXPECT_TRUE(queue1->NeedsPumping()); - - LazyNow lazy_now(now_src_.get()); - queue1->PumpQueue(&lazy_now, true); - EXPECT_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue0->NeedsPumping()); - EXPECT_TRUE(queue1->HasPendingImmediateWork()); - EXPECT_FALSE(queue1->NeedsPumping()); - - test_task_runner_->RunUntilIdle(); - EXPECT_FALSE(queue0->HasPendingImmediateWork()); - EXPECT_FALSE(queue0->NeedsPumping()); - EXPECT_FALSE(queue1->HasPendingImmediateWork()); - EXPECT_FALSE(queue1->NeedsPumping()); -} - -void ExpensiveTestTask(int value, - base::SimpleTestTickClock* clock, - std::vector<EnqueueOrder>* out_result) { - out_result->push_back(value); - clock->Advance(base::TimeDelta::FromMilliseconds(1)); -} - -TEST_F(TaskQueueManagerTest, ImmediateAndDelayedTaskInterleaving) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); - for (int i = 10; i < 19; i++) { - runners_[0]->PostDelayedTask( - FROM_HERE, - base::Bind(&ExpensiveTestTask, i, now_src_.get(), &run_order), - delay); - } - - test_task_runner_->RunForPeriod(delay); - - for (int i = 0; i < 9; i++) { - runners_[0]->PostTask( - FROM_HERE, - base::Bind(&ExpensiveTestTask, i, now_src_.get(), &run_order)); - } - - test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); - test_task_runner_->RunUntilIdle(); - - // Delayed tasks are not allowed to starve out immediate work which is why - // some of the immediate tasks run out of order. - int expected_run_order[] = { - 10, 11, 12, 13, 0, 14, 15, 16, 1, 17, 18, 2, 3, 4, 5, 6, 7, 8 - }; - EXPECT_THAT(run_order, ElementsAreArray(expected_run_order)); -} - -TEST_F(TaskQueueManagerTest, - DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_SameQueue) { - Initialize(1u); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay); - - now_src_->Advance(delay * 2); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(2, 3, 1)); -} - -TEST_F(TaskQueueManagerTest, - DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_DifferentQueues) { - Initialize(2u); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay); - - now_src_->Advance(delay * 2); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(2, 3, 1)); -} - -TEST_F(TaskQueueManagerTest, DelayedTaskDoesNotSkipAHeadOfShorterDelayedTask) { - Initialize(2u); - - std::vector<EnqueueOrder> run_order; - base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10); - base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(5); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - delay1); - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - delay2); - - now_src_->Advance(delay1 * 2); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(2, 1)); -} - -void CheckIsNested(bool* is_nested) { - *is_nested = base::MessageLoop::current()->IsNested(); -} - -void PostAndQuitFromNestedRunloop(base::RunLoop* run_loop, - base::SingleThreadTaskRunner* runner, - bool* was_nested) { - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - runner->PostTask(FROM_HERE, run_loop->QuitClosure()); - runner->PostTask(FROM_HERE, base::Bind(&CheckIsNested, was_nested)); - run_loop->Run(); -} - -TEST_F(TaskQueueManagerTest, QuitWhileNested) { - // This test makes sure we don't continue running a work batch after a nested - // run loop has been exited in the middle of the batch. - InitializeWithRealMessageLoop(1u); - manager_->SetWorkBatchSize(2); - - bool was_nested = true; - base::RunLoop run_loop; - runners_[0]->PostTask(FROM_HERE, base::Bind(&PostAndQuitFromNestedRunloop, - base::Unretained(&run_loop), - base::RetainedRef(runners_[0]), - base::Unretained(&was_nested))); - - message_loop_->RunUntilIdle(); - EXPECT_FALSE(was_nested); -} - -class SequenceNumberCapturingTaskObserver - : public base::MessageLoop::TaskObserver { - public: - // MessageLoop::TaskObserver overrides. - void WillProcessTask(const base::PendingTask& pending_task) override {} - void DidProcessTask(const base::PendingTask& pending_task) override { - sequence_numbers_.push_back(pending_task.sequence_num); - } - - const std::vector<EnqueueOrder>& sequence_numbers() const { - return sequence_numbers_; - } - - private: - std::vector<EnqueueOrder> sequence_numbers_; -}; - -TEST_F(TaskQueueManagerTest, SequenceNumSetWhenTaskIsPosted) { - Initialize(1u); - - SequenceNumberCapturingTaskObserver observer; - manager_->AddTaskObserver(&observer); - - // Register four tasks that will run in reverse order. - std::vector<EnqueueOrder> run_order; - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - base::TimeDelta::FromMilliseconds(30)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - base::TimeDelta::FromMilliseconds(20)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40)); - ASSERT_THAT(run_order, ElementsAre(4, 3, 2, 1)); - - // The sequence numbers are a zero-based monotonically incrememting counter - // which should be set when the task is posted rather than when it's enqueued - // onto the Incoming queue. - EXPECT_THAT(observer.sequence_numbers(), ElementsAre(3, 2, 1, 0)); - - manager_->RemoveTaskObserver(&observer); -} - -TEST_F(TaskQueueManagerTest, NewTaskQueues) { - Initialize(1u); - - scoped_refptr<internal::TaskQueueImpl> queue1 = - manager_->NewTaskQueue(TaskQueue::Spec("foo")); - scoped_refptr<internal::TaskQueueImpl> queue2 = - manager_->NewTaskQueue(TaskQueue::Spec("bar")); - scoped_refptr<internal::TaskQueueImpl> queue3 = - manager_->NewTaskQueue(TaskQueue::Spec("baz")); - - ASSERT_NE(queue1, queue2); - ASSERT_NE(queue1, queue3); - ASSERT_NE(queue2, queue3); - - std::vector<EnqueueOrder> run_order; - queue1->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - queue2->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - queue3->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); -} - -TEST_F(TaskQueueManagerTest, UnregisterTaskQueue) { - Initialize(1u); - - scoped_refptr<internal::TaskQueueImpl> queue1 = - manager_->NewTaskQueue(TaskQueue::Spec("foo")); - scoped_refptr<internal::TaskQueueImpl> queue2 = - manager_->NewTaskQueue(TaskQueue::Spec("bar")); - scoped_refptr<internal::TaskQueueImpl> queue3 = - manager_->NewTaskQueue(TaskQueue::Spec("baz")); - - ASSERT_NE(queue1, queue2); - ASSERT_NE(queue1, queue3); - ASSERT_NE(queue2, queue3); - - std::vector<EnqueueOrder> run_order; - queue1->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - queue2->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - queue3->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - - queue2->UnregisterTaskQueue(); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, ElementsAre(1, 3)); -} - -TEST_F(TaskQueueManagerTest, UnregisterTaskQueue_WithDelayedTasks) { - Initialize(2u); - - // Register three delayed tasks - std::vector<EnqueueOrder> run_order; - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - base::TimeDelta::FromMilliseconds(10)); - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - base::TimeDelta::FromMilliseconds(20)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - base::TimeDelta::FromMilliseconds(30)); - - runners_[1]->UnregisterTaskQueue(); - test_task_runner_->RunUntilIdle(); - - test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40)); - ASSERT_THAT(run_order, ElementsAre(1, 3)); -} - -namespace { -void UnregisterQueue(scoped_refptr<internal::TaskQueueImpl> queue) { - queue->UnregisterTaskQueue(); -} -} - -TEST_F(TaskQueueManagerTest, UnregisterTaskQueue_InTasks) { - Initialize(3u); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); - runners_[0]->PostTask(FROM_HERE, base::Bind(&UnregisterQueue, runners_[1])); - runners_[0]->PostTask(FROM_HERE, base::Bind(&UnregisterQueue, runners_[2])); - runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); - runners_[2]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); - - test_task_runner_->RunUntilIdle(); - ASSERT_THAT(run_order, ElementsAre(1)); -} - -void PostTestTasksFromNestedMessageLoop( - base::MessageLoop* message_loop, - scoped_refptr<base::SingleThreadTaskRunner> main_runner, - scoped_refptr<base::SingleThreadTaskRunner> wake_up_runner, - std::vector<EnqueueOrder>* run_order) { - base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); - main_runner->PostNonNestableTask(FROM_HERE, - base::Bind(&TestTask, 1, run_order)); - // The following should never get executed. - wake_up_runner->PostTask(FROM_HERE, base::Bind(&TestTask, 2, run_order)); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(TaskQueueManagerTest, DeferredNonNestableTaskDoesNotTriggerWakeUp) { - // This test checks that running (i.e., deferring) a non-nestable task in a - // nested run loop does not trigger the pumping of an on-wakeup queue. - InitializeWithRealMessageLoop(2u); - runners_[1]->SetPumpPolicy(TaskQueue::PumpPolicy::AFTER_WAKEUP); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostTask( - FROM_HERE, - base::Bind(&PostTestTasksFromNestedMessageLoop, message_loop_.get(), - runners_[0], runners_[1], base::Unretained(&run_order))); - - message_loop_->RunUntilIdle(); - ASSERT_THAT(run_order, ElementsAre(1)); -} - -namespace { - -class MockObserver : public TaskQueueManager::Observer { - public: - MOCK_METHOD1(OnUnregisterTaskQueue, - void(const scoped_refptr<TaskQueue>& queue)); - MOCK_METHOD2(OnTriedToExecuteBlockedTask, - void(const TaskQueue& queue, const base::PendingTask& task)); -}; - -} // namespace - -TEST_F(TaskQueueManagerTest, OnUnregisterTaskQueue) { - Initialize(0u); - - MockObserver observer; - manager_->SetObserver(&observer); - - scoped_refptr<internal::TaskQueueImpl> task_queue = - manager_->NewTaskQueue(TaskQueue::Spec("test_queue")); - - EXPECT_CALL(observer, OnUnregisterTaskQueue(_)).Times(1); - task_queue->UnregisterTaskQueue(); - - manager_->SetObserver(nullptr); -} - -TEST_F(TaskQueueManagerTest, OnTriedToExecuteBlockedTask) { - Initialize(0u); - - MockObserver observer; - manager_->SetObserver(&observer); - - scoped_refptr<internal::TaskQueueImpl> task_queue = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue").SetShouldReportWhenExecutionBlocked(true)); - task_queue->SetQueueEnabled(false); - task_queue->PostTask(FROM_HERE, base::Bind(&NopTask)); - - EXPECT_CALL(observer, OnTriedToExecuteBlockedTask(_, _)).Times(1); - test_task_runner_->RunPendingTasks(); - - manager_->SetObserver(nullptr); -} - -TEST_F(TaskQueueManagerTest, ExecutedNonBlockedTask) { - Initialize(0u); - - MockObserver observer; - manager_->SetObserver(&observer); - - scoped_refptr<internal::TaskQueueImpl> task_queue = manager_->NewTaskQueue( - TaskQueue::Spec("test_queue").SetShouldReportWhenExecutionBlocked(true)); - task_queue->PostTask(FROM_HERE, base::Bind(&NopTask)); - - EXPECT_CALL(observer, OnTriedToExecuteBlockedTask(_, _)).Times(0); - test_task_runner_->RunPendingTasks(); - - manager_->SetObserver(nullptr); -} - -void HasOneRefTask(std::vector<bool>* log, internal::TaskQueueImpl* tq) { - log->push_back(tq->HasOneRef()); -} - -TEST_F(TaskQueueManagerTest, UnregisterTaskQueueInNestedLoop) { - InitializeWithRealMessageLoop(1u); - - // We retain a reference to the task queue even when the manager has deleted - // its reference. - scoped_refptr<internal::TaskQueueImpl> task_queue = - manager_->NewTaskQueue(TaskQueue::Spec("test_queue")); - - std::vector<bool> log; - std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; - - // Inside a nested run loop, call task_queue->UnregisterTaskQueue, bookended - // by calls to HasOneRefTask to make sure the manager doesn't release its - // reference until the nested run loop exits. - // NB: This first HasOneRefTask is a sanity check. - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&HasOneRefTask, base::Unretained(&log), - base::Unretained(task_queue.get())), - true)); - tasks_to_post_from_nested_loop.push_back(std::make_pair( - base::Bind(&internal::TaskQueueImpl::UnregisterTaskQueue, - base::Unretained(task_queue.get())), true)); - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&HasOneRefTask, base::Unretained(&log), - base::Unretained(task_queue.get())), - true)); - runners_[0]->PostTask( - FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), - base::RetainedRef(runners_[0]), - base::Unretained(&tasks_to_post_from_nested_loop))); - message_loop_->RunUntilIdle(); - - // Add a final call to HasOneRefTask. This gives the manager a chance to - // release its reference, and checks that it has. - runners_[0]->PostTask(FROM_HERE, - base::Bind(&HasOneRefTask, base::Unretained(&log), - base::Unretained(task_queue.get()))); - message_loop_->RunUntilIdle(); - - EXPECT_THAT(log, ElementsAre(false, false, true)); -} - -TEST_F(TaskQueueManagerTest, TimeDomainsAreIndependant) { - Initialize(2u); - - base::TimeTicks start_time = manager_->delegate()->NowTicks(); - std::unique_ptr<VirtualTimeDomain> domain_a( - new VirtualTimeDomain(nullptr, start_time)); - std::unique_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<EnqueueOrder> run_order; - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - base::TimeDelta::FromMilliseconds(20)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - base::TimeDelta::FromMilliseconds(30)); - - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order), - base::TimeDelta::FromMilliseconds(10)); - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 5, &run_order), - base::TimeDelta::FromMilliseconds(20)); - runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 6, &run_order), - base::TimeDelta::FromMilliseconds(30)); - - domain_b->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50)); - manager_->MaybeScheduleImmediateWork(FROM_HERE); - - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(4, 5, 6)); - - domain_a->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50)); - manager_->MaybeScheduleImmediateWork(FROM_HERE); - - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(4, 5, 6, 1, 2, 3)); - - runners_[0]->UnregisterTaskQueue(); - runners_[1]->UnregisterTaskQueue(); - - manager_->UnregisterTimeDomain(domain_a.get()); - manager_->UnregisterTimeDomain(domain_b.get()); -} - -TEST_F(TaskQueueManagerTest, TimeDomainMigration) { - Initialize(1u); - - base::TimeTicks start_time = manager_->delegate()->NowTicks(); - std::unique_ptr<VirtualTimeDomain> domain_a( - new VirtualTimeDomain(nullptr, start_time)); - manager_->RegisterTimeDomain(domain_a.get()); - runners_[0]->SetTimeDomain(domain_a.get()); - - std::vector<EnqueueOrder> run_order; - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), - base::TimeDelta::FromMilliseconds(10)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), - base::TimeDelta::FromMilliseconds(20)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), - base::TimeDelta::FromMilliseconds(30)); - runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order), - base::TimeDelta::FromMilliseconds(40)); - - domain_a->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(20)); - manager_->MaybeScheduleImmediateWork(FROM_HERE); - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1, 2)); - - std::unique_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)); - manager_->MaybeScheduleImmediateWork(FROM_HERE); - - test_task_runner_->RunUntilIdle(); - EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); - - runners_[0]->UnregisterTaskQueue(); - - manager_->UnregisterTimeDomain(domain_a.get()); - manager_->UnregisterTimeDomain(domain_b.get()); -} - -namespace { -void ChromiumRunloopInspectionTask( - scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner) { - EXPECT_EQ(1u, test_task_runner->NumPendingTasks()); -} -} // namespace - -TEST_F(TaskQueueManagerTest, NumberOfPendingTasksOnChromiumRunLoop) { - Initialize(1u); - - // NOTE because tasks posted to the chromiumrun loop are not cancellable, we - // will end up with a lot more tasks posted if the delayed tasks were posted - // in the reverse order. - // TODO(alexclarke): Consider talking to the message pump directly. - test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); - for (int i = 1; i < 100; i++) { - runners_[0]->PostDelayedTask( - FROM_HERE, - base::Bind(&ChromiumRunloopInspectionTask, test_task_runner_), - base::TimeDelta::FromMilliseconds(i)); - } - test_task_runner_->RunUntilIdle(); -} - -namespace { - -class QuadraticTask { - public: - QuadraticTask(scoped_refptr<internal::TaskQueueImpl> task_queue, - base::TimeDelta delay, - base::SimpleTestTickClock* now_src) - : count_(0), task_queue_(task_queue), delay_(delay), now_src_(now_src) {} - - void SetShouldExit(base::Callback<bool()> should_exit) { - should_exit_ = should_exit; - } - - void Run() { - if (should_exit_.Run()) - return; - count_++; - task_queue_->PostDelayedTask( - FROM_HERE, base::Bind(&QuadraticTask::Run, base::Unretained(this)), - delay_); - task_queue_->PostDelayedTask( - FROM_HERE, base::Bind(&QuadraticTask::Run, base::Unretained(this)), - delay_); - now_src_->Advance(base::TimeDelta::FromMilliseconds(5)); - } - - int count() const { return count_; } - - private: - int count_; - scoped_refptr<internal::TaskQueueImpl> task_queue_; - base::TimeDelta delay_; - base::Callback<bool()> should_exit_; - base::SimpleTestTickClock* now_src_; -}; - -class LinearTask { - public: - LinearTask(scoped_refptr<internal::TaskQueueImpl> task_queue, - base::TimeDelta delay, - base::SimpleTestTickClock* now_src) - : count_(0), task_queue_(task_queue), delay_(delay), now_src_(now_src) {} - - void SetShouldExit(base::Callback<bool()> should_exit) { - should_exit_ = should_exit; - } - - void Run() { - if (should_exit_.Run()) - return; - count_++; - task_queue_->PostDelayedTask( - FROM_HERE, base::Bind(&LinearTask::Run, base::Unretained(this)), - delay_); - now_src_->Advance(base::TimeDelta::FromMilliseconds(5)); - } - - int count() const { return count_; } - - private: - int count_; - scoped_refptr<internal::TaskQueueImpl> task_queue_; - base::TimeDelta delay_; - base::Callback<bool()> should_exit_; - base::SimpleTestTickClock* now_src_; -}; - -bool ShouldExit(QuadraticTask* quadratic_task, LinearTask* linear_task) { - return quadratic_task->count() == 1000 || linear_task->count() == 1000; -} - -} // namespace - -TEST_F(TaskQueueManagerTest, - DelayedTasksDontBadlyStarveNonDelayedWork_SameQueue) { - Initialize(1u); - - QuadraticTask quadratic_delayed_task( - runners_[0], base::TimeDelta::FromMilliseconds(10), now_src_.get()); - LinearTask linear_immediate_task(runners_[0], base::TimeDelta(), - now_src_.get()); - base::Callback<bool()> should_exit = - base::Bind(ShouldExit, &quadratic_delayed_task, &linear_immediate_task); - quadratic_delayed_task.SetShouldExit(should_exit); - linear_immediate_task.SetShouldExit(should_exit); - - quadratic_delayed_task.Run(); - linear_immediate_task.Run(); - - test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); - test_task_runner_->RunUntilIdle(); - - double ratio = static_cast<double>(linear_immediate_task.count()) / - static_cast<double>(quadratic_delayed_task.count()); - - EXPECT_GT(ratio, 0.333); - EXPECT_LT(ratio, 1.1); -} - -TEST_F(TaskQueueManagerTest, ImmediateWorkCanStarveDelayedTasks_SameQueue) { - Initialize(1u); - - QuadraticTask quadratic_immediate_task(runners_[0], base::TimeDelta(), - now_src_.get()); - LinearTask linear_delayed_task( - runners_[0], base::TimeDelta::FromMilliseconds(10), now_src_.get()); - base::Callback<bool()> should_exit = - base::Bind(&ShouldExit, &quadratic_immediate_task, &linear_delayed_task); - - quadratic_immediate_task.SetShouldExit(should_exit); - linear_delayed_task.SetShouldExit(should_exit); - - quadratic_immediate_task.Run(); - linear_delayed_task.Run(); - - test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); - test_task_runner_->RunUntilIdle(); - - double ratio = static_cast<double>(linear_delayed_task.count()) / - static_cast<double>(quadratic_immediate_task.count()); - - // This is by design, we want to enforce a strict ordering in task execution - // where by delayed tasks can not skip ahead of non-delayed work. - EXPECT_GT(ratio, 0.0); - EXPECT_LT(ratio, 0.1); -} - -TEST_F(TaskQueueManagerTest, - DelayedTasksDontBadlyStarveNonDelayedWork_DifferentQueue) { - Initialize(2u); - - QuadraticTask quadratic_delayed_task( - runners_[0], base::TimeDelta::FromMilliseconds(10), now_src_.get()); - LinearTask linear_immediate_task(runners_[1], base::TimeDelta(), - now_src_.get()); - base::Callback<bool()> should_exit = - base::Bind(ShouldExit, &quadratic_delayed_task, &linear_immediate_task); - quadratic_delayed_task.SetShouldExit(should_exit); - linear_immediate_task.SetShouldExit(should_exit); - - quadratic_delayed_task.Run(); - linear_immediate_task.Run(); - - test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); - test_task_runner_->RunUntilIdle(); - - double ratio = static_cast<double>(linear_immediate_task.count()) / - static_cast<double>(quadratic_delayed_task.count()); - - EXPECT_GT(ratio, 0.333); - EXPECT_LT(ratio, 1.1); -} - -TEST_F(TaskQueueManagerTest, - ImmediateWorkCanStarveDelayedTasks_DifferentQueue) { - Initialize(2u); - - QuadraticTask quadratic_immediate_task(runners_[0], base::TimeDelta(), - now_src_.get()); - LinearTask linear_delayed_task( - runners_[1], base::TimeDelta::FromMilliseconds(10), now_src_.get()); - base::Callback<bool()> should_exit = - base::Bind(&ShouldExit, &quadratic_immediate_task, &linear_delayed_task); - - quadratic_immediate_task.SetShouldExit(should_exit); - linear_delayed_task.SetShouldExit(should_exit); - - quadratic_immediate_task.Run(); - linear_delayed_task.Run(); - - test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); - test_task_runner_->RunUntilIdle(); - - double ratio = static_cast<double>(linear_delayed_task.count()) / - static_cast<double>(quadratic_immediate_task.count()); - - // This is by design, we want to enforce a strict ordering in task execution - // where by delayed tasks can not skip ahead of non-delayed work. - EXPECT_GT(ratio, 0.0); - EXPECT_LT(ratio, 0.1); -} - -TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_NoTaskRunning) { - Initialize(1u); - - EXPECT_EQ(nullptr, manager_->currently_executing_task_queue()); -} - -namespace { -void CurrentlyExecutingTaskQueueTestTask(TaskQueueManager* task_queue_manager, - std::vector<TaskQueue*>* task_sources) { - task_sources->push_back(task_queue_manager->currently_executing_task_queue()); -} -} - -TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_TaskRunning) { - Initialize(2u); - - internal::TaskQueueImpl* queue0 = runners_[0].get(); - internal::TaskQueueImpl* queue1 = runners_[1].get(); - - std::vector<TaskQueue*> task_sources; - queue0->PostTask(FROM_HERE, base::Bind(&CurrentlyExecutingTaskQueueTestTask, - manager_.get(), &task_sources)); - queue1->PostTask(FROM_HERE, base::Bind(&CurrentlyExecutingTaskQueueTestTask, - manager_.get(), &task_sources)); - test_task_runner_->RunUntilIdle(); - - EXPECT_THAT(task_sources, ElementsAre(queue0, queue1)); - EXPECT_EQ(nullptr, manager_->currently_executing_task_queue()); -} - -namespace { -void RunloopCurrentlyExecutingTaskQueueTestTask( - base::MessageLoop* message_loop, - TaskQueueManager* task_queue_manager, - std::vector<TaskQueue*>* task_sources, - std::vector<std::pair<base::Closure, TaskQueue*>>* tasks) { - base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); - task_sources->push_back(task_queue_manager->currently_executing_task_queue()); - - for (std::pair<base::Closure, TaskQueue*>& pair : *tasks) { - pair.second->PostTask(FROM_HERE, pair.first); - } - - base::RunLoop().RunUntilIdle(); - task_sources->push_back(task_queue_manager->currently_executing_task_queue()); -} -} - -TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_NestedLoop) { - InitializeWithRealMessageLoop(3u); - - TaskQueue* queue0 = runners_[0].get(); - TaskQueue* queue1 = runners_[1].get(); - TaskQueue* queue2 = runners_[2].get(); - - std::vector<TaskQueue*> task_sources; - std::vector<std::pair<base::Closure, TaskQueue*>> - tasks_to_post_from_nested_loop; - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&CurrentlyExecutingTaskQueueTestTask, - manager_.get(), &task_sources), - queue1)); - tasks_to_post_from_nested_loop.push_back( - std::make_pair(base::Bind(&CurrentlyExecutingTaskQueueTestTask, - manager_.get(), &task_sources), - queue2)); - - queue0->PostTask( - FROM_HERE, base::Bind(&RunloopCurrentlyExecutingTaskQueueTestTask, - message_loop_.get(), manager_.get(), &task_sources, - &tasks_to_post_from_nested_loop)); - - message_loop_->RunUntilIdle(); - EXPECT_THAT(task_sources, ElementsAre(queue0, queue1, queue2, queue0)); - EXPECT_EQ(nullptr, manager_->currently_executing_task_queue()); -} - -void OnTraceDataCollected(base::Closure quit_closure, - base::trace_event::TraceResultBuffer* buffer, - const scoped_refptr<base::RefCountedString>& json, - bool has_more_events) { - buffer->AddFragment(json->data()); - if (!has_more_events) - quit_closure.Run(); -} - -class TaskQueueManagerTestWithTracing : public TaskQueueManagerTest { - public: - void StartTracing(); - void StopTracing(); - std::unique_ptr<trace_analyzer::TraceAnalyzer> CreateTraceAnalyzer(); -}; - -void TaskQueueManagerTestWithTracing::StartTracing() { - base::trace_event::TraceLog::GetInstance()->SetEnabled( - base::trace_event::TraceConfig("*"), - base::trace_event::TraceLog::RECORDING_MODE); -} - -void TaskQueueManagerTestWithTracing::StopTracing() { - base::trace_event::TraceLog::GetInstance()->SetDisabled(); -} - -std::unique_ptr<trace_analyzer::TraceAnalyzer> -TaskQueueManagerTestWithTracing::CreateTraceAnalyzer() { - base::trace_event::TraceResultBuffer buffer; - base::trace_event::TraceResultBuffer::SimpleOutput trace_output; - buffer.SetOutputCallback(trace_output.GetCallback()); - base::RunLoop run_loop; - buffer.Start(); - base::trace_event::TraceLog::GetInstance()->Flush( - Bind(&OnTraceDataCollected, run_loop.QuitClosure(), - base::Unretained(&buffer))); - run_loop.Run(); - buffer.Finish(); - - return base::WrapUnique( - trace_analyzer::TraceAnalyzer::Create(trace_output.json_output)); -} - -TEST_F(TaskQueueManagerTestWithTracing, BlameContextAttribution) { - using trace_analyzer::Query; - - InitializeWithRealMessageLoop(1u); - TaskQueue* queue = runners_[0].get(); - - StartTracing(); - { - base::trace_event::BlameContext blame_context("cat", "name", "type", - "scope", 0, nullptr); - blame_context.Initialize(); - queue->SetBlameContext(&blame_context); - queue->PostTask(FROM_HERE, base::Bind(&NopTask)); - message_loop_->RunUntilIdle(); - } - StopTracing(); - std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer = - CreateTraceAnalyzer(); - - trace_analyzer::TraceEventVector events; - Query q = Query::EventPhaseIs(TRACE_EVENT_PHASE_ENTER_CONTEXT) || - Query::EventPhaseIs(TRACE_EVENT_PHASE_LEAVE_CONTEXT); - analyzer->FindEvents(q, &events); - - EXPECT_EQ(2u, events.size()); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/task_queue_selector.cc b/chromium/components/scheduler/base/task_queue_selector.cc deleted file mode 100644 index 9db8ab9c2de..00000000000 --- a/chromium/components/scheduler/base/task_queue_selector.cc +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2014 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/base/task_queue_selector.h" - -#include "base/logging.h" -#include "base/trace_event/trace_event_argument.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/work_queue.h" - -namespace scheduler { -namespace internal { - -TaskQueueSelector::TaskQueueSelector() - : enabled_selector_(this, "enabled"), - blocked_selector_(this, "blocked"), - immediate_starvation_count_(0), - high_priority_starvation_count_(0), - num_blocked_queues_to_report_(0), - task_queue_selector_observer_(nullptr) {} - -TaskQueueSelector::~TaskQueueSelector() {} - -void TaskQueueSelector::AddQueue(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK(queue->IsQueueEnabled()); - enabled_selector_.AddQueue(queue, TaskQueue::NORMAL_PRIORITY); -} - -void TaskQueueSelector::RemoveQueue(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (queue->IsQueueEnabled()) { - enabled_selector_.RemoveQueue(queue); -// The #if DCHECK_IS_ON() shouldn't be necessary but this doesn't compile on -// chromeos bots without it :( -#if DCHECK_IS_ON() - DCHECK(!blocked_selector_.CheckContainsQueueForTest(queue)); -#endif - } else if (queue->should_report_when_execution_blocked()) { - DCHECK_GT(num_blocked_queues_to_report_, 0u); - num_blocked_queues_to_report_--; - blocked_selector_.RemoveQueue(queue); -#if DCHECK_IS_ON() - DCHECK(!enabled_selector_.CheckContainsQueueForTest(queue)); -#endif - } -} - -void TaskQueueSelector::EnableQueue(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK(queue->IsQueueEnabled()); - if (queue->should_report_when_execution_blocked()) { - DCHECK_GT(num_blocked_queues_to_report_, 0u); - num_blocked_queues_to_report_--; - blocked_selector_.RemoveQueue(queue); - } - enabled_selector_.AddQueue(queue, queue->GetQueuePriority()); - if (task_queue_selector_observer_) - task_queue_selector_observer_->OnTaskQueueEnabled(queue); -} - -void TaskQueueSelector::DisableQueue(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK(!queue->IsQueueEnabled()); - enabled_selector_.RemoveQueue(queue); - if (queue->should_report_when_execution_blocked()) { - blocked_selector_.AddQueue(queue, queue->GetQueuePriority()); - num_blocked_queues_to_report_++; - } -} - -void TaskQueueSelector::SetQueuePriority(internal::TaskQueueImpl* queue, - TaskQueue::QueuePriority priority) { - DCHECK_LT(priority, TaskQueue::QUEUE_PRIORITY_COUNT); - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (queue->IsQueueEnabled()) { - enabled_selector_.ChangeSetIndex(queue, priority); - } else if (queue->should_report_when_execution_blocked()) { - blocked_selector_.ChangeSetIndex(queue, priority); - } else { - // Normally blocked_selector_.ChangeSetIndex would assign the queue's - // priority, however if |queue->should_report_when_execution_blocked()| is - // false then the disabled queue is not in any set so we need to do it here. - queue->delayed_work_queue()->AssignSetIndex(priority); - queue->immediate_work_queue()->AssignSetIndex(priority); - } - DCHECK_EQ(priority, queue->GetQueuePriority()); -} - -TaskQueue::QueuePriority TaskQueueSelector::NextPriority( - TaskQueue::QueuePriority priority) { - DCHECK(priority < TaskQueue::QUEUE_PRIORITY_COUNT); - return static_cast<TaskQueue::QueuePriority>(static_cast<int>(priority) + 1); -} - -TaskQueueSelector::PrioritizingSelector::PrioritizingSelector( - TaskQueueSelector* task_queue_selector, - const char* name) - : task_queue_selector_(task_queue_selector), - delayed_work_queue_sets_(TaskQueue::QUEUE_PRIORITY_COUNT, name), - immediate_work_queue_sets_(TaskQueue::QUEUE_PRIORITY_COUNT, name) {} - -void TaskQueueSelector::PrioritizingSelector::AddQueue( - internal::TaskQueueImpl* queue, - TaskQueue::QueuePriority priority) { -#if DCHECK_IS_ON() - DCHECK(!CheckContainsQueueForTest(queue)); -#endif - delayed_work_queue_sets_.AddQueue(queue->delayed_work_queue(), priority); - immediate_work_queue_sets_.AddQueue(queue->immediate_work_queue(), priority); -#if DCHECK_IS_ON() - DCHECK(CheckContainsQueueForTest(queue)); -#endif -} - -void TaskQueueSelector::PrioritizingSelector::ChangeSetIndex( - internal::TaskQueueImpl* queue, - TaskQueue::QueuePriority priority) { -#if DCHECK_IS_ON() - DCHECK(CheckContainsQueueForTest(queue)); -#endif - delayed_work_queue_sets_.ChangeSetIndex(queue->delayed_work_queue(), - priority); - immediate_work_queue_sets_.ChangeSetIndex(queue->immediate_work_queue(), - priority); -#if DCHECK_IS_ON() - DCHECK(CheckContainsQueueForTest(queue)); -#endif -} - -void TaskQueueSelector::PrioritizingSelector::RemoveQueue( - internal::TaskQueueImpl* queue) { -#if DCHECK_IS_ON() - DCHECK(CheckContainsQueueForTest(queue)); -#endif - delayed_work_queue_sets_.RemoveQueue(queue->delayed_work_queue()); - immediate_work_queue_sets_.RemoveQueue(queue->immediate_work_queue()); - -#if DCHECK_IS_ON() - DCHECK(!CheckContainsQueueForTest(queue)); -#endif -} - -bool TaskQueueSelector::PrioritizingSelector:: - ChooseOldestImmediateTaskWithPriority(TaskQueue::QueuePriority priority, - WorkQueue** out_work_queue) const { - return immediate_work_queue_sets_.GetOldestQueueInSet(priority, - out_work_queue); -} - -bool TaskQueueSelector::PrioritizingSelector:: - ChooseOldestDelayedTaskWithPriority(TaskQueue::QueuePriority priority, - WorkQueue** out_work_queue) const { - return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue); -} - -bool TaskQueueSelector::PrioritizingSelector:: - ChooseOldestImmediateOrDelayedTaskWithPriority( - TaskQueue::QueuePriority priority, - bool* out_chose_delayed_over_immediate, - WorkQueue** out_work_queue) const { - WorkQueue* immediate_queue; - DCHECK_EQ(*out_chose_delayed_over_immediate, false); - if (immediate_work_queue_sets_.GetOldestQueueInSet(priority, - &immediate_queue)) { - WorkQueue* delayed_queue; - if (delayed_work_queue_sets_.GetOldestQueueInSet(priority, - &delayed_queue)) { - if (immediate_queue->ShouldRunBefore(delayed_queue)) { - *out_work_queue = immediate_queue; - } else { - *out_chose_delayed_over_immediate = true; - *out_work_queue = delayed_queue; - } - } else { - *out_work_queue = immediate_queue; - } - return true; - } - return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue); -} - -bool TaskQueueSelector::PrioritizingSelector::ChooseOldestWithPriority( - TaskQueue::QueuePriority priority, - bool* out_chose_delayed_over_immediate, - WorkQueue** out_work_queue) const { - // Select an immediate work queue if we are starving immediate tasks. - if (task_queue_selector_->immediate_starvation_count_ >= - kMaxDelayedStarvationTasks) { - if (ChooseOldestImmediateTaskWithPriority(priority, out_work_queue)) { - return true; - } - if (ChooseOldestDelayedTaskWithPriority(priority, out_work_queue)) { - return true; - } - return false; - } - return ChooseOldestImmediateOrDelayedTaskWithPriority( - priority, out_chose_delayed_over_immediate, out_work_queue); -} - -bool TaskQueueSelector::PrioritizingSelector::SelectWorkQueueToService( - TaskQueue::QueuePriority max_priority, - WorkQueue** out_work_queue, - bool* out_chose_delayed_over_immediate) { - DCHECK(task_queue_selector_->main_thread_checker_.CalledOnValidThread()); - DCHECK_EQ(*out_chose_delayed_over_immediate, false); - - // Always service the control queue if it has any work. - if (max_priority > TaskQueue::CONTROL_PRIORITY && - ChooseOldestWithPriority(TaskQueue::CONTROL_PRIORITY, - out_chose_delayed_over_immediate, - out_work_queue)) { - return true; - } - - // Select from the normal priority queue if we are starving it. - if (max_priority > TaskQueue::NORMAL_PRIORITY && - task_queue_selector_->high_priority_starvation_count_ >= - kMaxHighPriorityStarvationTasks && - ChooseOldestWithPriority(TaskQueue::NORMAL_PRIORITY, - out_chose_delayed_over_immediate, - out_work_queue)) { - return true; - } - // Otherwise choose in priority order. - for (TaskQueue::QueuePriority priority = TaskQueue::HIGH_PRIORITY; - priority < max_priority; priority = NextPriority(priority)) { - if (ChooseOldestWithPriority(priority, out_chose_delayed_over_immediate, - out_work_queue)) { - return true; - } - } - return false; -} - -#if DCHECK_IS_ON() || !defined(NDEBUG) -bool -TaskQueueSelector::PrioritizingSelector::CheckContainsQueueForTest( - const internal::TaskQueueImpl* queue) const { - bool contains_delayed_work_queue = - delayed_work_queue_sets_.ContainsWorkQueueForTest( - queue->delayed_work_queue()); - - bool contains_immediate_work_queue = - immediate_work_queue_sets_.ContainsWorkQueueForTest( - queue->immediate_work_queue()); - - DCHECK_EQ(contains_delayed_work_queue, contains_immediate_work_queue); - return contains_delayed_work_queue; -} -#endif - -bool TaskQueueSelector::SelectWorkQueueToService(WorkQueue** out_work_queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - bool chose_delayed_over_immediate = false; - bool found_queue = enabled_selector_.SelectWorkQueueToService( - TaskQueue::QUEUE_PRIORITY_COUNT, out_work_queue, - &chose_delayed_over_immediate); - if (!found_queue) { - TrySelectingBlockedQueue(); - return false; - } - - TrySelectingBlockedQueueOverEnabledQueue(**out_work_queue); - DidSelectQueueWithPriority( - (*out_work_queue)->task_queue()->GetQueuePriority(), - chose_delayed_over_immediate); - return true; -} - -void TaskQueueSelector::TrySelectingBlockedQueue() { - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (!num_blocked_queues_to_report_ || !task_queue_selector_observer_) - return; - WorkQueue* chosen_blocked_queue; - bool chose_delayed_over_immediate = false; - // There was nothing unblocked to run, see if we could have run a blocked - // task. - if (blocked_selector_.SelectWorkQueueToService( - TaskQueue::QUEUE_PRIORITY_COUNT, &chosen_blocked_queue, - &chose_delayed_over_immediate)) { - task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue( - chosen_blocked_queue); - } -} - -void TaskQueueSelector::TrySelectingBlockedQueueOverEnabledQueue( - const WorkQueue& chosen_enabled_queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (!num_blocked_queues_to_report_ || !task_queue_selector_observer_) - return; - - TaskQueue::QueuePriority max_priority = - NextPriority(chosen_enabled_queue.task_queue()->GetQueuePriority()); - - WorkQueue* chosen_blocked_queue; - bool chose_delayed_over_immediate = false; - bool found_queue = blocked_selector_.SelectWorkQueueToService( - max_priority, &chosen_blocked_queue, &chose_delayed_over_immediate); - if (!found_queue) - return; - - // Check if the chosen blocked queue has a lower numerical priority than the - // chosen enabled queue. If so we would have chosen the blocked queue (since - // zero is the highest priority). - if (chosen_blocked_queue->task_queue()->GetQueuePriority() < - chosen_enabled_queue.task_queue()->GetQueuePriority()) { - task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue( - chosen_blocked_queue); - return; - } - DCHECK_EQ(chosen_blocked_queue->task_queue()->GetQueuePriority(), - chosen_enabled_queue.task_queue()->GetQueuePriority()); - // Otherwise there was an enabled and a blocked task with the same priority. - // The one with the older enqueue order wins. - if (chosen_blocked_queue->ShouldRunBefore(&chosen_enabled_queue)) { - task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue( - chosen_blocked_queue); - } -} - -void TaskQueueSelector::DidSelectQueueWithPriority( - TaskQueue::QueuePriority priority, - bool chose_delayed_over_immediate) { - switch (priority) { - case TaskQueue::CONTROL_PRIORITY: - break; - case TaskQueue::HIGH_PRIORITY: - high_priority_starvation_count_++; - break; - case TaskQueue::NORMAL_PRIORITY: - case TaskQueue::BEST_EFFORT_PRIORITY: - high_priority_starvation_count_ = 0; - break; - default: - NOTREACHED(); - } - if (chose_delayed_over_immediate) { - immediate_starvation_count_++; - } else { - immediate_starvation_count_ = 0; - } -} - -void TaskQueueSelector::AsValueInto( - base::trace_event::TracedValue* state) const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - state->SetInteger("high_priority_starvation_count", - high_priority_starvation_count_); - state->SetInteger("immediate_starvation_count", immediate_starvation_count_); - state->SetInteger("num_blocked_queues_to_report", - num_blocked_queues_to_report_); -} - -void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) { - task_queue_selector_observer_ = observer; -} - -bool TaskQueueSelector::EnabledWorkQueuesEmpty() const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - for (TaskQueue::QueuePriority priority = TaskQueue::CONTROL_PRIORITY; - priority < TaskQueue::QUEUE_PRIORITY_COUNT; - priority = NextPriority(priority)) { - if (!enabled_selector_.delayed_work_queue_sets()->IsSetEmpty(priority) || - !enabled_selector_.immediate_work_queue_sets()->IsSetEmpty(priority)) { - return false; - } - } - return true; -} - -void TaskQueueSelector::SetImmediateStarvationCountForTest( - size_t immediate_starvation_count) { - immediate_starvation_count_ = immediate_starvation_count; -} - -} // namespace internal -} // namespace scheduler diff --git a/chromium/components/scheduler/base/task_queue_selector.h b/chromium/components/scheduler/base/task_queue_selector.h deleted file mode 100644 index 4a82adcfbd3..00000000000 --- a/chromium/components/scheduler/base/task_queue_selector.h +++ /dev/null @@ -1,206 +0,0 @@ -// 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_BASE_TASK_QUEUE_SELECTOR_H_ -#define COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_ - -#include <stddef.h> - -#include <set> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/pending_task.h" -#include "base/threading/thread_checker.h" -#include "components/scheduler/base/work_queue_sets.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { -namespace internal { - -// TaskQueueSelector is used by the SchedulerHelper to enable prioritization -// of particular task queues. -class SCHEDULER_EXPORT TaskQueueSelector { - public: - TaskQueueSelector(); - ~TaskQueueSelector(); - - // Called to register a queue that can be selected. This function is called - // on the main thread. - void AddQueue(internal::TaskQueueImpl* queue); - - // The specified work will no longer be considered for selection. This - // function is called on the main thread. - void RemoveQueue(internal::TaskQueueImpl* queue); - - // Make |queue| eligible for selection. This function is called on the main - // thread. Must only be called if |queue| is disabled. - void EnableQueue(internal::TaskQueueImpl* queue); - - // Disable selection from |queue|. If task blocking is enabled for the queue, - // Observer::OnTriedToSelectBlockedWorkQueue will be emitted if the - // SelectWorkQueueToService tries to select this disabled queue for execution. - // Must only be called if |queue| is enabled. - void DisableQueue(internal::TaskQueueImpl* queue); - - // Called get or set the priority of |queue|. - void SetQueuePriority(internal::TaskQueueImpl* queue, - TaskQueue::QueuePriority priority); - - // Called to choose the work queue from which the next task should be taken - // and run. Return true if |out_work_queue| indicates the queue to service or - // false to avoid running any task. - // - // This function is called on the main thread. - bool SelectWorkQueueToService(WorkQueue** out_work_queue); - - // Serialize the selector state for tracing. - void AsValueInto(base::trace_event::TracedValue* state) const; - - class SCHEDULER_EXPORT Observer { - public: - virtual ~Observer() {} - - // Called when |queue| transitions from disabled to enabled. - virtual void OnTaskQueueEnabled(internal::TaskQueueImpl* queue) = 0; - - // Called when the selector tried to select a task from a disabled work - // queue. See TaskQueue::Spec::SetShouldReportWhenExecutionBlocked. A single - // call to SelectWorkQueueToService will only result in up to one - // blocking notification even if multiple disabled queues could have been - // selected. - virtual void OnTriedToSelectBlockedWorkQueue( - internal::WorkQueue* work_queue) = 0; - }; - - // Called once to set the Observer. This function is called - // on the main thread. If |observer| is null, then no callbacks will occur. - void SetTaskQueueSelectorObserver(Observer* observer); - - // Returns true if all the enabled work queues are empty. Returns false - // otherwise. - bool EnabledWorkQueuesEmpty() const; - - protected: - class SCHEDULER_EXPORT PrioritizingSelector { - public: - PrioritizingSelector(TaskQueueSelector* task_queue_selector, - const char* name); - - void ChangeSetIndex(internal::TaskQueueImpl* queue, - TaskQueue::QueuePriority priority); - void AddQueue(internal::TaskQueueImpl* queue, - TaskQueue::QueuePriority priority); - void RemoveQueue(internal::TaskQueueImpl* queue); - - bool SelectWorkQueueToService(TaskQueue::QueuePriority max_priority, - WorkQueue** out_work_queue, - bool* out_chose_delayed_over_immediate); - - WorkQueueSets* delayed_work_queue_sets() { - return &delayed_work_queue_sets_; - } - WorkQueueSets* immediate_work_queue_sets() { - return &immediate_work_queue_sets_; - } - - const WorkQueueSets* delayed_work_queue_sets() const { - return &delayed_work_queue_sets_; - } - const WorkQueueSets* immediate_work_queue_sets() const { - return &immediate_work_queue_sets_; - } - - bool ChooseOldestWithPriority(TaskQueue::QueuePriority priority, - bool* out_chose_delayed_over_immediate, - WorkQueue** out_work_queue) const; - -#if DCHECK_IS_ON() || !defined(NDEBUG) - bool CheckContainsQueueForTest(const internal::TaskQueueImpl* queue) const; -#endif - - private: - bool ChooseOldestImmediateTaskWithPriority( - TaskQueue::QueuePriority priority, - WorkQueue** out_work_queue) const; - - bool ChooseOldestDelayedTaskWithPriority(TaskQueue::QueuePriority priority, - WorkQueue** out_work_queue) const; - - // Return true if |out_queue| contains the queue with the oldest pending - // task from the set of queues of |priority|, or false if all queues of that - // priority are empty. In addition |out_chose_delayed_over_immediate| is set - // to true iff we chose a delayed work queue in favour of an immediate work - // queue. - bool ChooseOldestImmediateOrDelayedTaskWithPriority( - TaskQueue::QueuePriority priority, - bool* out_chose_delayed_over_immediate, - WorkQueue** out_work_queue) const; - - const TaskQueueSelector* task_queue_selector_; - WorkQueueSets delayed_work_queue_sets_; - WorkQueueSets immediate_work_queue_sets_; - - DISALLOW_COPY_AND_ASSIGN(PrioritizingSelector); - }; - - // Return true if |out_queue| contains the queue with the oldest pending task - // from the set of queues of |priority|, or false if all queues of that - // priority are empty. In addition |out_chose_delayed_over_immediate| is set - // to true iff we chose a delayed work queue in favour of an immediate work - // queue. This method will force select an immediate task if those are being - // starved by delayed tasks. - void SetImmediateStarvationCountForTest(size_t immediate_starvation_count); - - PrioritizingSelector* enabled_selector_for_test() { - return &enabled_selector_; - } - - private: - // Returns the priority which is next after |priority|. - static TaskQueue::QueuePriority NextPriority( - TaskQueue::QueuePriority priority); - - bool SelectWorkQueueToServiceInternal(WorkQueue** out_work_queue); - - // Called whenever the selector chooses a task queue for execution with the - // priority |priority|. - void DidSelectQueueWithPriority(TaskQueue::QueuePriority priority, - bool chose_delayed_over_immediate); - - // No enabled queue could be selected, check if we could have chosen a - // disabled (blocked) work queue instead. - void TrySelectingBlockedQueue(); - - // Check if we could have chosen a disabled (blocked) work queue instead. - // |chosen_enabled_queue| is the enabled queue that got chosen. - void TrySelectingBlockedQueueOverEnabledQueue( - const WorkQueue& chosen_enabled_queue); - - // Number of high priority tasks which can be run before a normal priority - // task should be selected to prevent starvation. - // TODO(rmcilroy): Check if this is a good value. - static const size_t kMaxHighPriorityStarvationTasks = 5; - - // Maximum number of delayed tasks tasks which can be run while there's a - // waiting non-delayed task. - static const size_t kMaxDelayedStarvationTasks = 3; - - private: - base::ThreadChecker main_thread_checker_; - - PrioritizingSelector enabled_selector_; - PrioritizingSelector blocked_selector_; - size_t immediate_starvation_count_; - size_t high_priority_starvation_count_; - size_t num_blocked_queues_to_report_; - - Observer* task_queue_selector_observer_; // NOT OWNED - DISALLOW_COPY_AND_ASSIGN(TaskQueueSelector); -}; - -} // namespace internal -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H diff --git a/chromium/components/scheduler/base/task_queue_selector_unittest.cc b/chromium/components/scheduler/base/task_queue_selector_unittest.cc deleted file mode 100644 index 1be477d75f7..00000000000 --- a/chromium/components/scheduler/base/task_queue_selector_unittest.cc +++ /dev/null @@ -1,494 +0,0 @@ -// 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/base/task_queue_selector.h" - -#include <stddef.h> - -#include <memory> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/pending_task.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/virtual_time_domain.h" -#include "components/scheduler/base/work_queue.h" -#include "components/scheduler/base/work_queue_sets.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; - -namespace scheduler { -namespace internal { - -class MockObserver : public TaskQueueSelector::Observer { - public: - MockObserver() {} - virtual ~MockObserver() {} - - MOCK_METHOD1(OnTaskQueueEnabled, void(internal::TaskQueueImpl*)); - MOCK_METHOD1(OnTriedToSelectBlockedWorkQueue, void(internal::WorkQueue*)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockObserver); -}; - -class TaskQueueSelectorForTest : public TaskQueueSelector { - public: - using TaskQueueSelector::SetImmediateStarvationCountForTest; - using TaskQueueSelector::PrioritizingSelector; - using TaskQueueSelector::enabled_selector_for_test; -}; - -class TaskQueueSelectorTest : public testing::Test { - public: - TaskQueueSelectorTest() - : test_closure_(base::Bind(&TaskQueueSelectorTest::TestFunction)) {} - ~TaskQueueSelectorTest() override {} - - TaskQueueSelectorForTest::PrioritizingSelector* enabled_selector() { - return selector_.enabled_selector_for_test(); - } - - WorkQueueSets* delayed_work_queue_sets() { - return enabled_selector()->delayed_work_queue_sets(); - } - WorkQueueSets* immediate_work_queue_sets() { - return enabled_selector()->immediate_work_queue_sets(); - } - - void PushTasks(const size_t queue_indices[], size_t num_tasks) { - std::set<size_t> changed_queue_set; - for (size_t i = 0; i < num_tasks; i++) { - changed_queue_set.insert(queue_indices[i]); - task_queues_[queue_indices[i]]->immediate_work_queue()->Push( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), 0, - true, i)); - } - } - - void PushTasksWithEnqueueOrder(const size_t queue_indices[], - const size_t enqueue_orders[], - size_t num_tasks) { - std::set<size_t> changed_queue_set; - for (size_t i = 0; i < num_tasks; i++) { - changed_queue_set.insert(queue_indices[i]); - task_queues_[queue_indices[i]]->immediate_work_queue()->Push( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), 0, - true, enqueue_orders[i])); - } - } - - std::vector<size_t> PopTasks() { - std::vector<size_t> order; - WorkQueue* chosen_work_queue; - while (selector_.SelectWorkQueueToService(&chosen_work_queue)) { - size_t chosen_queue_index = - queue_to_index_map_.find(chosen_work_queue->task_queue())->second; - order.push_back(chosen_queue_index); - chosen_work_queue->PopTaskForTest(); - immediate_work_queue_sets()->OnPopQueue(chosen_work_queue); - } - return order; - } - - static void TestFunction() {} - - void EnableQueue(TaskQueueImpl* queue) { - queue->SetQueueEnabled(true); - selector_.EnableQueue(queue); - } - - void DisableQueue(TaskQueueImpl* queue) { - queue->SetQueueEnabled(false); - selector_.DisableQueue(queue); - } - - protected: - void SetUp() final { - virtual_time_domain_ = base::WrapUnique<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_.get(), - TaskQueue::Spec("test queue"), "test", "test")); - selector_.AddQueue(task_queue.get()); - task_queues_.push_back(task_queue); - } - for (size_t i = 0; i < kTaskQueueCount; i++) { - EXPECT_EQ(TaskQueue::NORMAL_PRIORITY, task_queues_[i]->GetQueuePriority()) - << i; - queue_to_index_map_.insert(std::make_pair(task_queues_[i].get(), i)); - } - } - - void TearDown() final { - for (scoped_refptr<TaskQueueImpl>& task_queue : task_queues_) { - task_queue->UnregisterTaskQueue(); - // Note since this test doesn't have a TaskQueueManager we need to - // manually remove |task_queue| from the |selector_|. Normally - // UnregisterTaskQueue would do that. - selector_.RemoveQueue(task_queue.get()); - } - } - - scoped_refptr<TaskQueueImpl> NewTaskQueueWithBlockReporting() { - return make_scoped_refptr(new TaskQueueImpl( - nullptr, virtual_time_domain_.get(), - TaskQueue::Spec("test queue").SetShouldReportWhenExecutionBlocked(true), - "test", "test")); - } - - const size_t kTaskQueueCount = 5; - base::Closure test_closure_; - TaskQueueSelectorForTest selector_; - std::unique_ptr<VirtualTimeDomain> virtual_time_domain_; - std::vector<scoped_refptr<TaskQueueImpl>> task_queues_; - std::map<TaskQueueImpl*, size_t> queue_to_index_map_; -}; - -TEST_F(TaskQueueSelectorTest, TestDefaultPriority) { - size_t queue_order[] = {4, 3, 2, 1, 0}; - PushTasks(queue_order, 5); - EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 3, 2, 1, 0)); -} - -TEST_F(TaskQueueSelectorTest, TestHighPriority) { - size_t queue_order[] = {0, 1, 2, 3, 4}; - PushTasks(queue_order, 5); - selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY); - EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 0, 1, 3, 4)); -} - -TEST_F(TaskQueueSelectorTest, TestBestEffortPriority) { - size_t queue_order[] = {0, 1, 2, 3, 4}; - PushTasks(queue_order, 5); - selector_.SetQueuePriority(task_queues_[0].get(), - TaskQueue::BEST_EFFORT_PRIORITY); - selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY); - EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 1, 3, 4, 0)); -} - -TEST_F(TaskQueueSelectorTest, TestControlPriority) { - size_t queue_order[] = {0, 1, 2, 3, 4}; - PushTasks(queue_order, 5); - selector_.SetQueuePriority(task_queues_[4].get(), - TaskQueue::CONTROL_PRIORITY); - EXPECT_EQ(TaskQueue::CONTROL_PRIORITY, task_queues_[4]->GetQueuePriority()); - selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY); - EXPECT_EQ(TaskQueue::HIGH_PRIORITY, task_queues_[2]->GetQueuePriority()); - EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 2, 0, 1, 3)); -} - -TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) { - DisableQueue(task_queues_[1].get()); - MockObserver mock_observer; - selector_.SetTaskQueueSelectorObserver(&mock_observer); - EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1); - EnableQueue(task_queues_[1].get()); -} - -TEST_F(TaskQueueSelectorTest, - TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled) { - selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::HIGH_PRIORITY); - MockObserver mock_observer; - selector_.SetTaskQueueSelectorObserver(&mock_observer); - EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(0); - selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::NORMAL_PRIORITY); -} - -TEST_F(TaskQueueSelectorTest, TestDisableEnable) { - MockObserver mock_observer; - selector_.SetTaskQueueSelectorObserver(&mock_observer); - - size_t queue_order[] = {0, 1, 2, 3, 4}; - PushTasks(queue_order, 5); - DisableQueue(task_queues_[2].get()); - DisableQueue(task_queues_[4].get()); - // Disabling a queue should not affect its priority. - EXPECT_EQ(TaskQueue::NORMAL_PRIORITY, task_queues_[2]->GetQueuePriority()); - EXPECT_EQ(TaskQueue::NORMAL_PRIORITY, task_queues_[4]->GetQueuePriority()); - EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3)); - - EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2); - EnableQueue(task_queues_[2].get()); - selector_.SetQueuePriority(task_queues_[2].get(), - TaskQueue::BEST_EFFORT_PRIORITY); - EXPECT_THAT(PopTasks(), testing::ElementsAre(2)); - EnableQueue(task_queues_[4].get()); - EXPECT_THAT(PopTasks(), testing::ElementsAre(4)); -} - -TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) { - EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty()); - EXPECT_TRUE(task_queues_[2]->immediate_work_queue()->Empty()); - - DisableQueue(task_queues_[2].get()); - selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY); - - size_t queue_order[] = {0, 1, 2, 3, 4}; - PushTasks(queue_order, 5); - - EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty()); - EXPECT_FALSE(task_queues_[2]->immediate_work_queue()->Empty()); - EnableQueue(task_queues_[2].get()); - - EXPECT_EQ(TaskQueue::HIGH_PRIORITY, task_queues_[2]->GetQueuePriority()); - EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 0, 1, 3, 4)); -} - -TEST_F(TaskQueueSelectorTest, TestEmptyQueues) { - WorkQueue* chosen_work_queue = nullptr; - EXPECT_FALSE(selector_.SelectWorkQueueToService(&chosen_work_queue)); - - // Test only disabled queues. - size_t queue_order[] = {0}; - PushTasks(queue_order, 1); - task_queues_[0]->SetQueueEnabled(false); - selector_.DisableQueue(task_queues_[0].get()); - EXPECT_FALSE(selector_.SelectWorkQueueToService(&chosen_work_queue)); -} - -TEST_F(TaskQueueSelectorTest, TestAge) { - size_t enqueue_order[] = {10, 1, 2, 9, 4}; - size_t queue_order[] = {0, 1, 2, 3, 4}; - PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5); - EXPECT_THAT(PopTasks(), testing::ElementsAre(1, 2, 4, 3, 0)); -} - -TEST_F(TaskQueueSelectorTest, TestControlStarvesOthers) { - size_t queue_order[] = {0, 1, 2, 3}; - PushTasks(queue_order, 4); - selector_.SetQueuePriority(task_queues_[3].get(), - TaskQueue::CONTROL_PRIORITY); - selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY); - selector_.SetQueuePriority(task_queues_[1].get(), - TaskQueue::BEST_EFFORT_PRIORITY); - for (int i = 0; i < 100; i++) { - WorkQueue* chosen_work_queue = nullptr; - EXPECT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue)); - EXPECT_EQ(task_queues_[3].get(), chosen_work_queue->task_queue()); - // Don't remove task from queue to simulate all queues still being full. - } -} - -TEST_F(TaskQueueSelectorTest, TestHighPriorityDoesNotStarveNormal) { - size_t queue_order[] = {0, 1, 2}; - PushTasks(queue_order, 3); - selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY); - selector_.SetQueuePriority(task_queues_[1].get(), - TaskQueue::BEST_EFFORT_PRIORITY); - size_t counts[] = {0, 0, 0}; - for (int i = 0; i < 100; i++) { - WorkQueue* chosen_work_queue = nullptr; - EXPECT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue)); - size_t chosen_queue_index = - queue_to_index_map_.find(chosen_work_queue->task_queue())->second; - counts[chosen_queue_index]++; - // Don't remove task from queue to simulate all queues still being full. - } - EXPECT_GT(counts[0], 0ul); // Check high doesn't starve normal. - EXPECT_GT(counts[2], counts[0]); // Check high gets more chance to run. - EXPECT_EQ(0ul, counts[1]); // Check best effort is starved. -} - -TEST_F(TaskQueueSelectorTest, TestBestEffortGetsStarved) { - size_t queue_order[] = {0, 1}; - PushTasks(queue_order, 2); - selector_.SetQueuePriority(task_queues_[0].get(), - TaskQueue::BEST_EFFORT_PRIORITY); - EXPECT_EQ(TaskQueue::NORMAL_PRIORITY, task_queues_[1]->GetQueuePriority()); - WorkQueue* chosen_work_queue = nullptr; - for (int i = 0; i < 100; i++) { - EXPECT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue)); - EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue()); - // Don't remove task from queue to simulate all queues still being full. - } - selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::HIGH_PRIORITY); - for (int i = 0; i < 100; i++) { - EXPECT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue)); - EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue()); - // Don't remove task from queue to simulate all queues still being full. - } - selector_.SetQueuePriority(task_queues_[1].get(), - TaskQueue::CONTROL_PRIORITY); - for (int i = 0; i < 100; i++) { - EXPECT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue)); - EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue()); - // Don't remove task from queue to simulate all queues still being full. - } -} - -TEST_F(TaskQueueSelectorTest, EnabledWorkQueuesEmpty) { - EXPECT_TRUE(selector_.EnabledWorkQueuesEmpty()); - size_t queue_order[] = {0, 1}; - PushTasks(queue_order, 2); - - EXPECT_FALSE(selector_.EnabledWorkQueuesEmpty()); - PopTasks(); - EXPECT_TRUE(selector_.EnabledWorkQueuesEmpty()); -} - -TEST_F(TaskQueueSelectorTest, EnabledWorkQueuesEmpty_ControlPriority) { - size_t queue_order[] = {0}; - PushTasks(queue_order, 1); - - selector_.SetQueuePriority(task_queues_[0].get(), - TaskQueue::CONTROL_PRIORITY); - - EXPECT_FALSE(selector_.EnabledWorkQueuesEmpty()); -} - -TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_Empty) { - WorkQueue* chosen_work_queue = nullptr; - bool chose_delayed_over_immediate = false; - EXPECT_FALSE(enabled_selector()->ChooseOldestWithPriority( - TaskQueue::NORMAL_PRIORITY, &chose_delayed_over_immediate, - &chosen_work_queue)); - EXPECT_FALSE(chose_delayed_over_immediate); -} - -TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_OnlyDelayed) { - task_queues_[0]->delayed_work_queue()->Push(TaskQueueImpl::Task( - FROM_HERE, test_closure_, base::TimeTicks(), 0, true, 0)); - - WorkQueue* chosen_work_queue = nullptr; - bool chose_delayed_over_immediate = false; - EXPECT_TRUE(enabled_selector()->ChooseOldestWithPriority( - TaskQueue::NORMAL_PRIORITY, &chose_delayed_over_immediate, - &chosen_work_queue)); - EXPECT_EQ(chosen_work_queue, task_queues_[0]->delayed_work_queue()); - EXPECT_FALSE(chose_delayed_over_immediate); -} - -TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_OnlyImmediate) { - task_queues_[0]->immediate_work_queue()->Push(TaskQueueImpl::Task( - FROM_HERE, test_closure_, base::TimeTicks(), 0, true, 0)); - - WorkQueue* chosen_work_queue = nullptr; - bool chose_delayed_over_immediate = false; - EXPECT_TRUE(enabled_selector()->ChooseOldestWithPriority( - TaskQueue::NORMAL_PRIORITY, &chose_delayed_over_immediate, - &chosen_work_queue)); - EXPECT_EQ(chosen_work_queue, task_queues_[0]->immediate_work_queue()); - EXPECT_FALSE(chose_delayed_over_immediate); -} - -TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) { - TaskQueueSelectorForTest selector; - MockObserver mock_observer; - selector.SetTaskQueueSelectorObserver(&mock_observer); - - scoped_refptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting()); - selector.AddQueue(task_queue.get()); - task_queue->SetQueueEnabled(false); - selector.DisableQueue(task_queue.get()); - - task_queue->immediate_work_queue()->PushAndSetEnqueueOrder( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), 0, true), - 0); - - WorkQueue* chosen_work_queue; - EXPECT_CALL(mock_observer, OnTriedToSelectBlockedWorkQueue(_)).Times(1); - EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue)); - - task_queue->UnregisterTaskQueue(); - selector.RemoveQueue(task_queue.get()); -} - -TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) { - TaskQueueSelectorForTest selector; - MockObserver mock_observer; - selector.SetTaskQueueSelectorObserver(&mock_observer); - - scoped_refptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting()); - scoped_refptr<TaskQueueImpl> task_queue2(NewTaskQueueWithBlockReporting()); - selector.AddQueue(task_queue.get()); - selector.AddQueue(task_queue2.get()); - task_queue->SetQueueEnabled(false); - task_queue2->SetQueueEnabled(false); - selector.DisableQueue(task_queue.get()); - selector.DisableQueue(task_queue2.get()); - selector.SetQueuePriority(task_queue2.get(), TaskQueue::CONTROL_PRIORITY); - - task_queue->immediate_work_queue()->PushAndSetEnqueueOrder( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), 0, true), - 0); - task_queue2->immediate_work_queue()->PushAndSetEnqueueOrder( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), 0, true), - 0); - - // Should still only see one call to OnTriedToSelectBlockedWorkQueue. - WorkQueue* chosen_work_queue; - EXPECT_CALL(mock_observer, OnTriedToSelectBlockedWorkQueue(_)).Times(1); - EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue)); - testing::Mock::VerifyAndClearExpectations(&mock_observer); - - // Removing the second queue and selecting again should result in another - // notification. - task_queue->UnregisterTaskQueue(); - selector.RemoveQueue(task_queue.get()); - EXPECT_CALL(mock_observer, OnTriedToSelectBlockedWorkQueue(_)).Times(1); - EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue)); - - task_queue2->UnregisterTaskQueue(); - selector.RemoveQueue(task_queue2.get()); -} - -struct ChooseOldestWithPriorityTestParam { - int delayed_task_enqueue_order; - int immediate_task_enqueue_order; - int immediate_starvation_count; - const char* expected_work_queue_name; - bool expected_did_starve_immediate_queue; -}; - -static const ChooseOldestWithPriorityTestParam - kChooseOldestWithPriorityTestCases[] = { - {1, 2, 0, "delayed", true}, - {1, 2, 1, "delayed", true}, - {1, 2, 2, "delayed", true}, - {1, 2, 3, "immediate", false}, - {1, 2, 4, "immediate", false}, - {2, 1, 4, "immediate", false}, - {2, 1, 4, "immediate", false}, -}; - -class ChooseOldestWithPriorityTest - : public TaskQueueSelectorTest, - public testing::WithParamInterface<ChooseOldestWithPriorityTestParam> {}; - -TEST_P(ChooseOldestWithPriorityTest, RoundRobinTest) { - task_queues_[0]->immediate_work_queue()->Push( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), - GetParam().immediate_task_enqueue_order, true, - GetParam().immediate_task_enqueue_order)); - - task_queues_[0]->delayed_work_queue()->Push( - TaskQueueImpl::Task(FROM_HERE, test_closure_, base::TimeTicks(), - GetParam().delayed_task_enqueue_order, true, - GetParam().delayed_task_enqueue_order)); - - selector_.SetImmediateStarvationCountForTest( - GetParam().immediate_starvation_count); - - WorkQueue* chosen_work_queue = nullptr; - bool chose_delayed_over_immediate = false; - EXPECT_TRUE(enabled_selector()->ChooseOldestWithPriority( - TaskQueue::NORMAL_PRIORITY, &chose_delayed_over_immediate, - &chosen_work_queue)); - EXPECT_EQ(chosen_work_queue->task_queue(), task_queues_[0].get()); - EXPECT_STREQ(chosen_work_queue->name(), GetParam().expected_work_queue_name); - EXPECT_EQ(chose_delayed_over_immediate, - GetParam().expected_did_starve_immediate_queue); -} - -INSTANTIATE_TEST_CASE_P(ChooseOldestWithPriorityTest, - ChooseOldestWithPriorityTest, - testing::ValuesIn(kChooseOldestWithPriorityTestCases)); - -} // namespace internal -} // namespace scheduler diff --git a/chromium/components/scheduler/base/test_always_fail_time_source.cc b/chromium/components/scheduler/base/test_always_fail_time_source.cc deleted file mode 100644 index 254298453f9..00000000000 --- a/chromium/components/scheduler/base/test_always_fail_time_source.cc +++ /dev/null @@ -1,21 +0,0 @@ -// 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/base/test_always_fail_time_source.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace scheduler { - -TestAlwaysFailTimeSource::TestAlwaysFailTimeSource() { -} - -TestAlwaysFailTimeSource::~TestAlwaysFailTimeSource() { -} - -base::TimeTicks TestAlwaysFailTimeSource::NowTicks() { - ADD_FAILURE() << "NowTicks() was called!"; - return base::TimeTicks(); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/test_always_fail_time_source.h b/chromium/components/scheduler/base/test_always_fail_time_source.h deleted file mode 100644 index ec6e73ddf81..00000000000 --- a/chromium/components/scheduler/base/test_always_fail_time_source.h +++ /dev/null @@ -1,26 +0,0 @@ -// 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_BASE_TEST_ALWAYS_FAIL_TIME_SOURCE_H_ -#define COMPONENTS_SCHEDULER_BASE_TEST_ALWAYS_FAIL_TIME_SOURCE_H_ - -#include "base/macros.h" -#include "base/time/tick_clock.h" - -namespace scheduler { - -class TestAlwaysFailTimeSource : public base::TickClock { - public: - explicit TestAlwaysFailTimeSource(); - ~TestAlwaysFailTimeSource() override; - - base::TimeTicks NowTicks() override; - - private: - DISALLOW_COPY_AND_ASSIGN(TestAlwaysFailTimeSource); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TEST_ALWAYS_FAIL_TIME_SOURCE_H_ diff --git a/chromium/components/scheduler/base/test_time_source.cc b/chromium/components/scheduler/base/test_time_source.cc deleted file mode 100644 index 6bbe83ba2a9..00000000000 --- a/chromium/components/scheduler/base/test_time_source.cc +++ /dev/null @@ -1,20 +0,0 @@ -// 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/base/test_time_source.h" - -namespace scheduler { - -TestTimeSource::TestTimeSource(base::SimpleTestTickClock* time_source) - : time_source_(time_source) { - DCHECK(time_source_); -} - -TestTimeSource::~TestTimeSource() {} - -base::TimeTicks TestTimeSource::NowTicks() { - return time_source_->NowTicks(); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/test_time_source.h b/chromium/components/scheduler/base/test_time_source.h deleted file mode 100644 index 9ff57f0202e..00000000000 --- a/chromium/components/scheduler/base/test_time_source.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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_BASE_TEST_TIME_SOURCE_H_ -#define COMPONENTS_SCHEDULER_BASE_TEST_TIME_SOURCE_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/test/simple_test_tick_clock.h" -#include "base/time/tick_clock.h" - -namespace scheduler { - -class TestTimeSource : public base::TickClock { - public: - explicit TestTimeSource(base::SimpleTestTickClock* time_source); - ~TestTimeSource() override; - - base::TimeTicks NowTicks() override; - - private: - // Not owned. - base::SimpleTestTickClock* time_source_; - - DISALLOW_COPY_AND_ASSIGN(TestTimeSource); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TEST_TIME_SOURCE_H_ diff --git a/chromium/components/scheduler/base/time_domain.cc b/chromium/components/scheduler/base/time_domain.cc deleted file mode 100644 index 61851c533ea..00000000000 --- a/chromium/components/scheduler/base/time_domain.cc +++ /dev/null @@ -1,215 +0,0 @@ -// 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/base/time_domain.h" - -#include <set> - -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager_delegate.h" -#include "components/scheduler/base/work_queue.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { - -TimeDomain::TimeDomain(Observer* observer) : observer_(observer) {} - -TimeDomain::~TimeDomain() { - DCHECK(main_thread_checker_.CalledOnValidThread()); -} - -void TimeDomain::RegisterQueue(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK_EQ(queue->GetTimeDomain(), this); -} - -void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK_EQ(queue->GetTimeDomain(), this); - UnregisterAsUpdatableTaskQueue(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(); - iter != delayed_wakeup_multimap_.end();) { - if (iter->second == queue) { - // O(1) amortized. - iter = delayed_wakeup_multimap_.erase(iter); - } else { - iter++; - } - } -} - -void TimeDomain::MigrateQueue(internal::TaskQueueImpl* queue, - TimeDomain* destination_time_domain) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK_EQ(queue->GetTimeDomain(), this); - DCHECK(destination_time_domain); - UnregisterAsUpdatableTaskQueue(queue); - - base::TimeTicks destination_now = destination_time_domain->Now(); - // 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(); - iter != delayed_wakeup_multimap_.end();) { - if (iter->second == queue) { - destination_time_domain->ScheduleDelayedWork(queue, iter->first, - destination_now); - // O(1) amortized. - iter = delayed_wakeup_multimap_.erase(iter); - } else { - iter++; - } - } -} - -void TimeDomain::ScheduleDelayedWork(internal::TaskQueueImpl* queue, - base::TimeTicks delayed_run_time, - base::TimeTicks now) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (delayed_wakeup_multimap_.empty() || - delayed_run_time < delayed_wakeup_multimap_.begin()->first) { - base::TimeDelta delay = std::max(base::TimeDelta(), delayed_run_time - now); - RequestWakeup(now, delay); - } - - delayed_wakeup_multimap_.insert(std::make_pair(delayed_run_time, queue)); - if (observer_) - observer_->OnTimeDomainHasDelayedWork(); -} - -void TimeDomain::RegisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue) { - { - base::AutoLock lock(newly_updatable_lock_); - newly_updatable_.push_back(queue); - } - if (observer_) - observer_->OnTimeDomainHasImmediateWork(); -} - -void TimeDomain::UnregisterAsUpdatableTaskQueue( - internal::TaskQueueImpl* queue) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - - updatable_queue_set_.erase(queue); - - base::AutoLock lock(newly_updatable_lock_); - // Remove all copies of |queue| from |newly_updatable_|. - for (size_t i = 0; i < newly_updatable_.size();) { - if (newly_updatable_[i] == queue) { - // Move last element into slot #i and then compact. - newly_updatable_[i] = newly_updatable_.back(); - newly_updatable_.pop_back(); - } else { - i++; - } - } -} - -void TimeDomain::UpdateWorkQueues( - bool should_trigger_wakeup, - const internal::TaskQueueImpl::Task* previous_task) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - LazyNow lazy_now(CreateLazyNow()); - - // Move any ready delayed tasks into the Incoming queues. - WakeupReadyDelayedQueues(&lazy_now, should_trigger_wakeup, previous_task); - - MoveNewlyUpdatableQueuesIntoUpdatableQueueSet(); - - auto iter = updatable_queue_set_.begin(); - while (iter != updatable_queue_set_.end()) { - internal::TaskQueueImpl* queue = *iter++; - // NOTE Update work queue may erase itself from |updatable_queue_set_|. - // This is fine, erasing an element won't invalidate any interator, as long - // as the iterator isn't the element being delated. - if (queue->immediate_work_queue()->Empty()) - queue->UpdateImmediateWorkQueue(should_trigger_wakeup, previous_task); - } -} - -void TimeDomain::MoveNewlyUpdatableQueuesIntoUpdatableQueueSet() { - DCHECK(main_thread_checker_.CalledOnValidThread()); - base::AutoLock lock(newly_updatable_lock_); - while (!newly_updatable_.empty()) { - updatable_queue_set_.insert(newly_updatable_.back()); - newly_updatable_.pop_back(); - } -} - -void TimeDomain::WakeupReadyDelayedQueues( - LazyNow* lazy_now, - bool should_trigger_wakeup, - const internal::TaskQueueImpl::Task* previous_task) { - DCHECK(main_thread_checker_.CalledOnValidThread()); - // Wake up any queues with pending delayed work. Note std::multipmap stores - // the elements sorted by key, so the begin() iterator points to the earliest - // queue to wakeup. - std::set<internal::TaskQueueImpl*> dedup_set; - while (!delayed_wakeup_multimap_.empty()) { - DelayedWakeupMultimap::iterator next_wakeup = - delayed_wakeup_multimap_.begin(); - if (next_wakeup->first > lazy_now->Now()) - break; - // A queue could have any number of delayed tasks pending so it's worthwhile - // deduping calls to UpdateDelayedWorkQueue since it takes a lock. - // NOTE the order in which these are called matters since the order - // in which EnqueueTaskLocks is called is respected when choosing which - // queue to execute a task from. - if (dedup_set.insert(next_wakeup->second).second) { - next_wakeup->second->UpdateDelayedWorkQueue( - lazy_now, should_trigger_wakeup, previous_task); - } - delayed_wakeup_multimap_.erase(next_wakeup); - } -} - -void TimeDomain::ClearExpiredWakeups() { - DCHECK(main_thread_checker_.CalledOnValidThread()); - LazyNow lazy_now(CreateLazyNow()); - while (!delayed_wakeup_multimap_.empty()) { - DelayedWakeupMultimap::iterator next_wakeup = - delayed_wakeup_multimap_.begin(); - if (next_wakeup->first > lazy_now.Now()) - break; - delayed_wakeup_multimap_.erase(next_wakeup); - } -} - -bool TimeDomain::NextScheduledRunTime(base::TimeTicks* out_time) const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (delayed_wakeup_multimap_.empty()) - return false; - - *out_time = delayed_wakeup_multimap_.begin()->first; - return true; -} - -bool TimeDomain::NextScheduledTaskQueue(TaskQueue** out_task_queue) const { - DCHECK(main_thread_checker_.CalledOnValidThread()); - if (delayed_wakeup_multimap_.empty()) - return false; - - *out_task_queue = delayed_wakeup_multimap_.begin()->second; - return true; -} - -void TimeDomain::AsValueInto(base::trace_event::TracedValue* state) const { - state->BeginDictionary(); - state->SetString("name", GetName()); - state->BeginArray("updatable_queue_set"); - for (auto& queue : updatable_queue_set_) - state->AppendString(queue->GetName()); - state->EndArray(); - state->SetInteger("registered_delay_count", delayed_wakeup_multimap_.size()); - if (!delayed_wakeup_multimap_.empty()) { - base::TimeDelta delay = delayed_wakeup_multimap_.begin()->first - Now(); - state->SetDouble("next_delay_ms", delay.InMillisecondsF()); - } - AsValueIntoInternal(state); - state->EndDictionary(); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/time_domain.h b/chromium/components/scheduler/base/time_domain.h deleted file mode 100644 index 0f33d811bad..00000000000 --- a/chromium/components/scheduler/base/time_domain.h +++ /dev/null @@ -1,156 +0,0 @@ -// 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_BASE_TIME_DOMAIN_H_ -#define COMPONENTS_SCHEDULER_BASE_TIME_DOMAIN_H_ - -#include <map> - -#include "base/callback.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "components/scheduler/base/lazy_now.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { -namespace internal { -class TaskQueueImpl; -} // internal -class TaskQueueManager; -class TaskQueueManagerDelegate; - -class SCHEDULER_EXPORT TimeDomain { - public: - 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. - 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. - // TODO(alexclarke): Make this main thread only. - virtual LazyNow CreateLazyNow() const = 0; - - // Evaluate this TimeDomain's Now. Can be called from any thread. - virtual base::TimeTicks Now() const = 0; - - // Some TimeDomains support virtual time, this method tells us to advance time - // if possible and return true if time was advanced. - virtual bool MaybeAdvanceTime() = 0; - - // Returns the name of this time domain for tracing. - virtual const char* GetName() const = 0; - - // If there is a scheduled delayed task, |out_time| is set to the scheduled - // runtime for the next one and it returns true. Returns false otherwise. - bool NextScheduledRunTime(base::TimeTicks* out_time) const; - - protected: - friend class internal::TaskQueueImpl; - friend class TaskQueueManager; - - void AsValueInto(base::trace_event::TracedValue* state) const; - - // Migrates |queue| from this time domain to |destination_time_domain|. - void MigrateQueue(internal::TaskQueueImpl* queue, - TimeDomain* destination_time_domain); - - // If there is a scheduled delayed task, |out_task_queue| is set to the queue - // the next task was posted to and it returns true. Returns false otherwise. - bool NextScheduledTaskQueue(TaskQueue** out_task_queue) const; - - // Adds |queue| to the set of task queues that UpdateWorkQueues calls - // UpdateWorkQueue on. - void RegisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue); - - // Schedules a call to TaskQueueImpl::MoveReadyDelayedTasksToDelayedWorkQueue - // when this TimeDomain reaches |delayed_run_time|. - void ScheduleDelayedWork(internal::TaskQueueImpl* queue, - base::TimeTicks delayed_run_time, - base::TimeTicks 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); - - // Removes |queue| from all internal data structures. - void UnregisterQueue(internal::TaskQueueImpl* queue); - - // Updates active queues associated with this TimeDomain. - void UpdateWorkQueues(bool should_trigger_wakeup, - const internal::TaskQueueImpl::Task* previous_task); - - // Called by the TaskQueueManager when the TimeDomain is registered. - virtual void OnRegisterWithTaskQueueManager( - TaskQueueManager* task_queue_manager) = 0; - - // The implementaion will secedule task processing to run with |delay| with - // respect to the TimeDomain's time source. Always called on the main thread. - // NOTE this is only called by ScheduleDelayedWork if the scheduled runtime - // is sooner than any previously sheduled work or if there is no other - // scheduled work. - virtual void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) = 0; - - // For implementation specific tracing. - virtual void AsValueIntoInternal( - base::trace_event::TracedValue* state) const = 0; - - // Call TaskQueueImpl::UpdateDelayedWorkQueue for each queue where the delay - // has elapsed. - void WakeupReadyDelayedQueues( - LazyNow* lazy_now, - bool should_trigger_wakeup, - const internal::TaskQueueImpl::Task* previous_task); - - protected: - // Clears expired entries from |delayed_wakeup_multimap_|. Caution needs to be - // taken to ensure TaskQueueImpl::UpdateDelayedWorkQueue or - // TaskQueueImpl::Pump is called on the affected queues. - void ClearExpiredWakeups(); - - private: - void MoveNewlyUpdatableQueuesIntoUpdatableQueueSet(); - - typedef std::multimap<base::TimeTicks, internal::TaskQueueImpl*> - DelayedWakeupMultimap; - - DelayedWakeupMultimap delayed_wakeup_multimap_; - - // This lock guards only |newly_updatable_|. It's not expected to be heavily - // contended. - base::Lock newly_updatable_lock_; - std::vector<internal::TaskQueueImpl*> newly_updatable_; - - // Set of task queues with avaliable work on the incoming queue. This should - // only be accessed from the main thread. - std::set<internal::TaskQueueImpl*> updatable_queue_set_; - - Observer* observer_; // NOT OWNED. - - base::ThreadChecker main_thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(TimeDomain); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_TIME_DOMAIN_H_ diff --git a/chromium/components/scheduler/base/time_domain_unittest.cc b/chromium/components/scheduler/base/time_domain_unittest.cc deleted file mode 100644 index 39fc93997a1..00000000000 --- a/chromium/components/scheduler/base/time_domain_unittest.cc +++ /dev/null @@ -1,230 +0,0 @@ -// 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/base/time_domain.h" - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/test/simple_test_tick_clock.h" -#include "cc/test/ordered_simple_task_runner.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager.h" -#include "components/scheduler/base/task_queue_manager_delegate_for_test.h" -#include "components/scheduler/base/test_time_source.h" -#include "components/scheduler/base/work_queue.h" -#include "testing/gmock/include/gmock/gmock.h" - -using testing::_; -using testing::AnyNumber; -using testing::Mock; - -namespace scheduler { - -class MockTimeDomain : public TimeDomain { - public: - explicit MockTimeDomain(TimeDomain::Observer* observer) - : TimeDomain(observer), - now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {} - - ~MockTimeDomain() override {} - - using TimeDomain::ClearExpiredWakeups; - using TimeDomain::NextScheduledRunTime; - using TimeDomain::NextScheduledTaskQueue; - using TimeDomain::ScheduleDelayedWork; - using TimeDomain::UnregisterQueue; - using TimeDomain::UpdateWorkQueues; - using TimeDomain::RegisterAsUpdatableTaskQueue; - - // TimeSource implementation: - LazyNow CreateLazyNow() const override { return LazyNow(now_); } - base::TimeTicks Now() const override { return now_; } - - void AsValueIntoInternal( - base::trace_event::TracedValue* state) const override {} - - bool MaybeAdvanceTime() override { return false; } - const char* GetName() const override { return "Test"; } - void OnRegisterWithTaskQueueManager( - TaskQueueManager* task_queue_manager) override {} - - MOCK_METHOD2(RequestWakeup, void(base::TimeTicks now, base::TimeDelta delay)); - - void SetNow(base::TimeTicks now) { now_ = now; } - - - private: - base::TimeTicks now_; - - DISALLOW_COPY_AND_ASSIGN(MockTimeDomain); -}; - -class TimeDomainTest : public testing::Test { - public: - void SetUp() final { - time_domain_ = base::WrapUnique(CreateMockTimeDomain()); - task_queue_ = make_scoped_refptr(new internal::TaskQueueImpl( - nullptr, time_domain_.get(), TaskQueue::Spec("test_queue"), - "test.category", "test.category")); - } - - void TearDown() final { - if (task_queue_) - task_queue_->UnregisterTaskQueue(); - } - - virtual MockTimeDomain* CreateMockTimeDomain() { - return new MockTimeDomain(nullptr); - } - - std::unique_ptr<MockTimeDomain> time_domain_; - scoped_refptr<internal::TaskQueueImpl> task_queue_; -}; - -TEST_F(TimeDomainTest, ScheduleDelayedWork) { - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks delayed_runtime = time_domain_->Now() + delay; - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay)); - base::TimeTicks now = time_domain_->Now(); - time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay, now); - - base::TimeTicks next_scheduled_runtime; - EXPECT_TRUE(time_domain_->NextScheduledRunTime(&next_scheduled_runtime)); - EXPECT_EQ(delayed_runtime, next_scheduled_runtime); - - TaskQueue* next_task_queue; - EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); - EXPECT_EQ(task_queue_.get(), next_task_queue); -} - -TEST_F(TimeDomainTest, RequestWakeup_OnlyCalledForEarlierTasks) { - base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10); - base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(20); - base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(30); - base::TimeDelta delay4 = base::TimeDelta::FromMilliseconds(1); - - // RequestWakeup should always be called if there are no other wakeups. - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay1)); - base::TimeTicks now = time_domain_->Now(); - time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay1, now); - - Mock::VerifyAndClearExpectations(time_domain_.get()); - - // RequestWakeup should not be called when scheduling later tasks. - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(0); - time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay2, now); - time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay3, now); - - // RequestWakeup should be called when scheduling earlier tasks. - Mock::VerifyAndClearExpectations(time_domain_.get()); - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay4)); - time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay4, now); -} - -TEST_F(TimeDomainTest, UnregisterQueue) { - scoped_refptr<internal::TaskQueueImpl> task_queue2_ = - make_scoped_refptr(new internal::TaskQueueImpl( - nullptr, time_domain_.get(), TaskQueue::Spec("test_queue2"), - "test.category", "test.category")); - - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(1); - base::TimeTicks now = time_domain_->Now(); - time_domain_->ScheduleDelayedWork( - task_queue_.get(), now + base::TimeDelta::FromMilliseconds(10), now); - time_domain_->ScheduleDelayedWork( - task_queue2_.get(), now + base::TimeDelta::FromMilliseconds(100), now); - - TaskQueue* next_task_queue; - EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); - EXPECT_EQ(task_queue_.get(), next_task_queue); - - time_domain_->UnregisterQueue(task_queue_.get()); - task_queue_ = scoped_refptr<internal::TaskQueueImpl>(); - EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); - EXPECT_EQ(task_queue2_.get(), next_task_queue); - - time_domain_->UnregisterQueue(task_queue2_.get()); - EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); -} - -TEST_F(TimeDomainTest, UpdateWorkQueues) { - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50); - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay)); - base::TimeTicks now = time_domain_->Now(); - base::TimeTicks delayed_runtime = now + delay; - time_domain_->ScheduleDelayedWork(task_queue_.get(), delayed_runtime, now); - - base::TimeTicks next_run_time; - ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time)); - EXPECT_EQ(delayed_runtime, next_run_time); - - time_domain_->UpdateWorkQueues(false, nullptr); - ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time)); - EXPECT_EQ(delayed_runtime, next_run_time); - - time_domain_->SetNow(delayed_runtime); - time_domain_->UpdateWorkQueues(false, nullptr); - ASSERT_FALSE(time_domain_->NextScheduledRunTime(&next_run_time)); -} - -TEST_F(TimeDomainTest, ClearExpiredWakeups) { - base::TimeTicks now = time_domain_->Now(); - base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10); - base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(20); - base::TimeTicks run_time1 = now + delay1; - base::TimeTicks run_time2 = now + delay2; - - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(AnyNumber()); - time_domain_->ScheduleDelayedWork(task_queue_.get(), run_time1, now); - time_domain_->ScheduleDelayedWork(task_queue_.get(), run_time2, now); - - base::TimeTicks next_run_time; - ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time)); - EXPECT_EQ(run_time1, next_run_time); - - time_domain_->SetNow(run_time1); - time_domain_->ClearExpiredWakeups(); - - ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time)); - EXPECT_EQ(run_time2, next_run_time); - - time_domain_->SetNow(run_time2); - time_domain_->ClearExpiredWakeups(); - ASSERT_FALSE(time_domain_->NextScheduledRunTime(&next_run_time)); -} - -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()); - } - - std::unique_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(_, _)); - base::TimeTicks now = time_domain_->Now(); - time_domain_->ScheduleDelayedWork( - task_queue_.get(), now + base::TimeDelta::FromMilliseconds(10), now); -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/virtual_time_domain.cc b/chromium/components/scheduler/base/virtual_time_domain.cc deleted file mode 100644 index db9c4035b29..00000000000 --- a/chromium/components/scheduler/base/virtual_time_domain.cc +++ /dev/null @@ -1,64 +0,0 @@ -// 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/base/virtual_time_domain.h" - -#include "base/bind.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/base/task_queue_manager.h" -#include "components/scheduler/base/task_queue_manager_delegate.h" - -namespace scheduler { - -VirtualTimeDomain::VirtualTimeDomain(TimeDomain::Observer* observer, - base::TimeTicks initial_time) - : TimeDomain(observer), now_(initial_time), task_queue_manager_(nullptr) {} - -VirtualTimeDomain::~VirtualTimeDomain() {} - -void VirtualTimeDomain::OnRegisterWithTaskQueueManager( - TaskQueueManager* task_queue_manager) { - task_queue_manager_ = task_queue_manager; - DCHECK(task_queue_manager_); -} - -LazyNow VirtualTimeDomain::CreateLazyNow() const { - base::AutoLock lock(lock_); - return LazyNow(now_); -} - -base::TimeTicks VirtualTimeDomain::Now() const { - base::AutoLock lock(lock_); - return now_; -} - -void VirtualTimeDomain::RequestWakeup(base::TimeTicks now, - base::TimeDelta delay) { - // We don't need to do anything here because the caller of AdvanceTo is - // responsible for calling TaskQueueManager::MaybeScheduleImmediateWork if - // needed. -} - -bool VirtualTimeDomain::MaybeAdvanceTime() { - return false; -} - -void VirtualTimeDomain::AsValueIntoInternal( - base::trace_event::TracedValue* state) const {} - -void VirtualTimeDomain::AdvanceTo(base::TimeTicks now) { - base::AutoLock lock(lock_); - DCHECK_GE(now, now_); - now_ = now; -} - -void VirtualTimeDomain::RequestDoWork() { - task_queue_manager_->MaybeScheduleImmediateWork(FROM_HERE); -} - -const char* VirtualTimeDomain::GetName() const { - return "VirtualTimeDomain"; -} - -} // namespace scheduler diff --git a/chromium/components/scheduler/base/virtual_time_domain.h b/chromium/components/scheduler/base/virtual_time_domain.h deleted file mode 100644 index e522533be66..00000000000 --- a/chromium/components/scheduler/base/virtual_time_domain.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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_BASE_VIRTUAL_TIME_DOMAIN_H_ -#define COMPONENTS_SCHEDULER_BASE_VIRTUAL_TIME_DOMAIN_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "components/scheduler/base/time_domain.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { - -class SCHEDULER_EXPORT VirtualTimeDomain : public TimeDomain { - public: - VirtualTimeDomain(TimeDomain::Observer* observer, - base::TimeTicks initial_time); - ~VirtualTimeDomain() override; - - // TimeDomain implementation: - LazyNow CreateLazyNow() const override; - base::TimeTicks Now() const override; - bool MaybeAdvanceTime() override; - const char* GetName() const override; - - // Advances this time domain to |now|. NOTE |now| is supposed to be - // monotonically increasing. NOTE it's the responsibility of the caller to - // call TaskQueueManager::MaybeScheduleImmediateWork if needed. - void AdvanceTo(base::TimeTicks now); - - using TimeDomain::ClearExpiredWakeups; - - protected: - void OnRegisterWithTaskQueueManager( - TaskQueueManager* task_queue_manager) override; - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override; - void AsValueIntoInternal( - base::trace_event::TracedValue* state) const override; - - void RequestDoWork(); - - private: - mutable base::Lock lock_; // Protects |now_|. - base::TimeTicks now_; - - TaskQueueManager* task_queue_manager_; // NOT OWNED - base::Closure do_work_closure_; - - DISALLOW_COPY_AND_ASSIGN(VirtualTimeDomain); -}; - -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_VIRTUAL_TIME_DOMAIN_H_ diff --git a/chromium/components/scheduler/base/work_queue.cc b/chromium/components/scheduler/base/work_queue.cc deleted file mode 100644 index 313baa9af36..00000000000 --- a/chromium/components/scheduler/base/work_queue.cc +++ /dev/null @@ -1,105 +0,0 @@ -// 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/base/work_queue.h" - -#include "components/scheduler/base/work_queue_sets.h" - -namespace scheduler { -namespace internal { - -WorkQueue::WorkQueue(TaskQueueImpl* task_queue, const char* name) - : work_queue_sets_(nullptr), - task_queue_(task_queue), - work_queue_set_index_(0), - name_(name) {} - -void WorkQueue::AsValueInto(base::trace_event::TracedValue* state) const { - std::queue<TaskQueueImpl::Task> queue_copy(work_queue_); - while (!queue_copy.empty()) { - TaskQueueImpl::TaskAsValueInto(queue_copy.front(), state); - queue_copy.pop(); - } -} - -WorkQueue::~WorkQueue() { - DCHECK(!work_queue_sets_) << task_queue_ ->GetName() << " : " - << work_queue_sets_->name() << " : " << name_; -} - -const TaskQueueImpl::Task* WorkQueue::GetFrontTask() const { - if (work_queue_.empty()) - return nullptr; - return &work_queue_.front(); -} - -bool WorkQueue::GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const { - if (work_queue_.empty()) - return false; - *enqueue_order = work_queue_.front().enqueue_order(); - return true; -} - -void WorkQueue::Push(const TaskQueueImpl::Task& task) { - bool was_empty = work_queue_.empty(); - work_queue_.push(task); - if (was_empty && work_queue_sets_) - work_queue_sets_->OnPushQueue(this); -} - -void WorkQueue::PushAndSetEnqueueOrder(const TaskQueueImpl::Task& task, - EnqueueOrder enqueue_order) { - bool was_empty = work_queue_.empty(); - work_queue_.push(task); - work_queue_.back().set_enqueue_order(enqueue_order); - - if (was_empty && work_queue_sets_) - work_queue_sets_->OnPushQueue(this); -} - -void WorkQueue::PopTaskForTest() { - work_queue_.pop(); -} - -void WorkQueue::SwapLocked(std::queue<TaskQueueImpl::Task>& incoming_queue) { - std::swap(work_queue_, incoming_queue); - - if (!work_queue_.empty() && work_queue_sets_) - work_queue_sets_->OnPushQueue(this); - task_queue_->TraceQueueSize(true); -} - -TaskQueueImpl::Task WorkQueue::TakeTaskFromWorkQueue() { - DCHECK(work_queue_sets_); - DCHECK(!work_queue_.empty()); - TaskQueueImpl::Task pending_task = std::move(work_queue_.front()); - work_queue_.pop(); - work_queue_sets_->OnPopQueue(this); - task_queue_->TraceQueueSize(false); - return pending_task; -} - -void WorkQueue::AssignToWorkQueueSets(WorkQueueSets* work_queue_sets) { - work_queue_sets_ = work_queue_sets; -} - -void WorkQueue::AssignSetIndex(size_t work_queue_set_index) { - work_queue_set_index_ = work_queue_set_index; -} - -bool WorkQueue::ShouldRunBefore(const WorkQueue* other_queue) const { - DCHECK(!work_queue_.empty()); - DCHECK(!other_queue->work_queue_.empty()); - EnqueueOrder enqueue_order = 0; - EnqueueOrder other_enqueue_order = 0; - bool have_task = GetFrontTaskEnqueueOrder(&enqueue_order); - bool have_other_task = - other_queue->GetFrontTaskEnqueueOrder(&other_enqueue_order); - DCHECK(have_task); - DCHECK(have_other_task); - return enqueue_order < other_enqueue_order; -} - -} // namespace internal -} // namespace scheduler diff --git a/chromium/components/scheduler/base/work_queue.h b/chromium/components/scheduler/base/work_queue.h deleted file mode 100644 index 9ff919d7f7e..00000000000 --- a/chromium/components/scheduler/base/work_queue.h +++ /dev/null @@ -1,96 +0,0 @@ -// 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 CONTENT_RENDERER_SCHEDULER_BASE_WORK_QUEUE_H_ -#define CONTENT_RENDERER_SCHEDULER_BASE_WORK_QUEUE_H_ - -#include <stddef.h> - -#include <set> - -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_argument.h" -#include "components/scheduler/base/enqueue_order.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { -namespace internal { -class WorkQueueSets; - -class SCHEDULER_EXPORT WorkQueue { - public: - WorkQueue(TaskQueueImpl* task_queue, const char* name); - ~WorkQueue(); - - // Associates this work queue with the given work queue sets. This must be - // called before any tasks can be inserted into this work queue. - void AssignToWorkQueueSets(WorkQueueSets* work_queue_sets); - - // Assigns the current set index. - void AssignSetIndex(size_t work_queue_set_index); - - void AsValueInto(base::trace_event::TracedValue* state) const; - - // Clears the |work_queue_|. - void Clear(); - - // returns true if the |work_queue_| is empty. - bool Empty() const { return work_queue_.empty(); } - - // If the |work_queue_| isn't empty, |enqueue_order| gets set to the enqueue - // order of the front task and the function returns true. Otherwise the - // function returns false. - bool GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const; - - // Returns the first task in this queue or null if the queue is empty. - const TaskQueueImpl::Task* GetFrontTask() const; - - // Pushes the task onto the |work_queue_| and informs the WorkQueueSets if - // the head changed. - void Push(const TaskQueueImpl::Task& task); - - // Pushes the task onto the |work_queue_|, sets the |enqueue_order| and - // informs the WorkQueueSets if the head changed. - void PushAndSetEnqueueOrder(const TaskQueueImpl::Task& task, - EnqueueOrder enqueue_order); - - // Swap the |work_queue_| with |incoming_queue| and informs the - // WorkQueueSets if the head changed. Assumes |task_queue_->any_thread_lock_| - // is locked. - void SwapLocked(std::queue<TaskQueueImpl::Task>& incoming_queue); - - size_t Size() const { return work_queue_.size(); } - - // Pulls a task off the |work_queue_| and informs the WorkQueueSets. - TaskQueueImpl::Task TakeTaskFromWorkQueue(); - - const char* name() const { return name_; } - - TaskQueueImpl* task_queue() const { return task_queue_; } - - WorkQueueSets* work_queue_sets() const { return work_queue_sets_; } - - size_t work_queue_set_index() const { return work_queue_set_index_; } - - // Test support function. This should not be used in production code. - void PopTaskForTest(); - - // Returns true if the front task in this queue has an older enqueue order - // than the front task of |other_queue|. Both queue are assumed to be - // non-empty. - bool ShouldRunBefore(const WorkQueue* other_queue) const; - - private: - std::queue<TaskQueueImpl::Task> work_queue_; - WorkQueueSets* work_queue_sets_; // NOT OWNED. - TaskQueueImpl* task_queue_; // NOT OWNED. - size_t work_queue_set_index_; - const char* name_; -}; - -} // namespace internal -} // namespace scheduler - -#endif // CONTENT_RENDERER_SCHEDULER_BASE_WORK_QUEUE_H_ diff --git a/chromium/components/scheduler/base/work_queue_sets.cc b/chromium/components/scheduler/base/work_queue_sets.cc deleted file mode 100644 index 96c5da4a01f..00000000000 --- a/chromium/components/scheduler/base/work_queue_sets.cc +++ /dev/null @@ -1,146 +0,0 @@ -// 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/base/work_queue_sets.h" - -#include "base/logging.h" -#include "components/scheduler/base/work_queue.h" - -namespace scheduler { -namespace internal { - -WorkQueueSets::WorkQueueSets(size_t num_sets, const char* name) - : enqueue_order_to_work_queue_maps_(num_sets), name_(name) {} - -WorkQueueSets::~WorkQueueSets() {} - -void WorkQueueSets::AddQueue(WorkQueue* work_queue, size_t set_index) { - DCHECK(!work_queue->work_queue_sets()); - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()); - EnqueueOrder enqueue_order; - bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order); - work_queue->AssignToWorkQueueSets(this); - work_queue->AssignSetIndex(set_index); - if (!has_enqueue_order) - return; - enqueue_order_to_work_queue_maps_[set_index].insert( - std::make_pair(enqueue_order, work_queue)); -} - -void WorkQueueSets::RemoveQueue(WorkQueue* work_queue) { - DCHECK_EQ(this, work_queue->work_queue_sets()); - EnqueueOrder enqueue_order; - bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order); - work_queue->AssignToWorkQueueSets(nullptr); - if (!has_enqueue_order) - return; - size_t set_index = work_queue->work_queue_set_index(); - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()); - DCHECK_EQ( - work_queue, - enqueue_order_to_work_queue_maps_[set_index].find(enqueue_order)->second); - enqueue_order_to_work_queue_maps_[set_index].erase(enqueue_order); -} - -void WorkQueueSets::ChangeSetIndex(WorkQueue* work_queue, size_t set_index) { - DCHECK_EQ(this, work_queue->work_queue_sets()); - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()); - EnqueueOrder enqueue_order; - bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order); - size_t old_set = work_queue->work_queue_set_index(); - DCHECK_LT(old_set, enqueue_order_to_work_queue_maps_.size()); - DCHECK_NE(old_set, set_index); - work_queue->AssignSetIndex(set_index); - if (!has_enqueue_order) - return; - enqueue_order_to_work_queue_maps_[old_set].erase(enqueue_order); - enqueue_order_to_work_queue_maps_[set_index].insert( - std::make_pair(enqueue_order, work_queue)); -} - -void WorkQueueSets::OnPushQueue(WorkQueue* work_queue) { - // NOTE if this funciton changes, we need to keep |WorkQueueSets::AddQueue| in - // sync. - DCHECK_EQ(this, work_queue->work_queue_sets()); - EnqueueOrder enqueue_order; - bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order); - DCHECK(has_enqueue_order); - size_t set_index = work_queue->work_queue_set_index(); - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()) - << " set_index = " << set_index; - enqueue_order_to_work_queue_maps_[set_index].insert( - std::make_pair(enqueue_order, work_queue)); -} - -void WorkQueueSets::OnPopQueue(WorkQueue* work_queue) { - size_t set_index = work_queue->work_queue_set_index(); - DCHECK_EQ(this, work_queue->work_queue_sets()); - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()); - DCHECK(!enqueue_order_to_work_queue_maps_[set_index].empty()) - << " set_index = " << set_index; - DCHECK_EQ(enqueue_order_to_work_queue_maps_[set_index].begin()->second, - work_queue) - << " set_index = " << set_index; - // O(1) amortised. - enqueue_order_to_work_queue_maps_[set_index].erase( - enqueue_order_to_work_queue_maps_[set_index].begin()); - EnqueueOrder enqueue_order; - bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order); - if (!has_enqueue_order) - return; - enqueue_order_to_work_queue_maps_[set_index].insert( - std::make_pair(enqueue_order, work_queue)); -} - -bool WorkQueueSets::GetOldestQueueInSet(size_t set_index, - WorkQueue** out_work_queue) const { - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()); - if (enqueue_order_to_work_queue_maps_[set_index].empty()) - return false; - *out_work_queue = - enqueue_order_to_work_queue_maps_[set_index].begin()->second; -#ifndef NDEBUG - EnqueueOrder enqueue_order; - DCHECK((*out_work_queue)->GetFrontTaskEnqueueOrder(&enqueue_order)); - DCHECK_EQ(enqueue_order, - enqueue_order_to_work_queue_maps_[set_index].begin()->first); -#endif - return true; -} - -bool WorkQueueSets::IsSetEmpty(size_t set_index) const { - DCHECK_LT(set_index, enqueue_order_to_work_queue_maps_.size()) - << " set_index = " << set_index; - return enqueue_order_to_work_queue_maps_[set_index].empty(); -} - -#if DCHECK_IS_ON() || !defined(NDEBUG) -bool WorkQueueSets::ContainsWorkQueueForTest( - const WorkQueue* work_queue) const { - EnqueueOrder enqueue_order; - bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order); - - for (const EnqueueOrderToWorkQueueMap& map : - enqueue_order_to_work_queue_maps_) { - for (const EnqueueOrderToWorkQueueMap::value_type& key_value_pair : map) { - if (key_value_pair.second == work_queue) { - DCHECK(has_enqueue_order); - DCHECK_EQ(key_value_pair.first, enqueue_order); - DCHECK_EQ(this, work_queue->work_queue_sets()); - return true; - } - } - } - - if (work_queue->work_queue_sets() == this) { - DCHECK(!has_enqueue_order); - return true; - } - - return false; -} -#endif - -} // namespace internal -} // namespace scheduler diff --git a/chromium/components/scheduler/base/work_queue_sets.h b/chromium/components/scheduler/base/work_queue_sets.h deleted file mode 100644 index 0c2c2dcf2c1..00000000000 --- a/chromium/components/scheduler/base/work_queue_sets.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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_BASE_WORK_QUEUE_SETS_H_ -#define COMPONENTS_SCHEDULER_BASE_WORK_QUEUE_SETS_H_ - -#include <stddef.h> - -#include <map> -#include <vector> - -#include "base/logging.h" -#include "base/macros.h" -#include "base/trace_event/trace_event_argument.h" -#include "components/scheduler/base/task_queue_impl.h" -#include "components/scheduler/scheduler_export.h" - -namespace scheduler { -namespace internal { -class TaskQueueImpl; - -class SCHEDULER_EXPORT WorkQueueSets { - public: - WorkQueueSets(size_t num_sets, const char* name); - ~WorkQueueSets(); - - // O(log num queues) - void AddQueue(WorkQueue* queue, size_t set_index); - - // O(log num queues) - void RemoveQueue(WorkQueue* work_queue); - - // O(log num queues) - void ChangeSetIndex(WorkQueue* queue, size_t set_index); - - // O(log num queues) - void OnPushQueue(WorkQueue* work_queue); - - // If empty it's O(1) amortized, otherwise it's O(log num queues) - void OnPopQueue(WorkQueue* work_queue); - - // O(1) - bool GetOldestQueueInSet(size_t set_index, WorkQueue** out_work_queue) const; - - // O(1) - bool IsSetEmpty(size_t set_index) const; - -#if DCHECK_IS_ON() || !defined(NDEBUG) - // Note this iterates over everything in |enqueue_order_to_work_queue_maps_|. - // It's intended for use with DCHECKS and for testing - bool ContainsWorkQueueForTest(const WorkQueue* queue) const; -#endif - - const char* name() const { return name_; } - - private: - typedef std::map<EnqueueOrder, WorkQueue*> EnqueueOrderToWorkQueueMap; - std::vector<EnqueueOrderToWorkQueueMap> enqueue_order_to_work_queue_maps_; - const char* name_; - - DISALLOW_COPY_AND_ASSIGN(WorkQueueSets); -}; - -} // namespace internal -} // namespace scheduler - -#endif // COMPONENTS_SCHEDULER_BASE_WORK_QUEUE_SETS_H_ diff --git a/chromium/components/scheduler/base/work_queue_sets_unittest.cc b/chromium/components/scheduler/base/work_queue_sets_unittest.cc deleted file mode 100644 index 4466db1f981..00000000000 --- a/chromium/components/scheduler/base/work_queue_sets_unittest.cc +++ /dev/null @@ -1,259 +0,0 @@ -// 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/base/work_queue_sets.h" - -#include <stddef.h> - -#include "base/memory/ptr_util.h" -#include "components/scheduler/base/work_queue.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace scheduler { -class TimeDomain; - -namespace internal { - -class WorkQueueSetsTest : public testing::Test { - public: - void SetUp() override { - work_queue_sets_.reset(new WorkQueueSets(kNumSets, "test")); - } - - void TearDown() override { - for (std::unique_ptr<WorkQueue>& work_queue : work_queues_) { - if (work_queue->work_queue_sets()) - work_queue_sets_->RemoveQueue(work_queue.get()); - } - } - - protected: - enum { - kNumSets = 5 // An arbitary choice. - }; - - WorkQueue* NewTaskQueue(const char* queue_name) { - WorkQueue* queue = new WorkQueue(nullptr, "test"); - work_queues_.push_back(base::WrapUnique(queue)); - work_queue_sets_->AddQueue(queue, TaskQueue::CONTROL_PRIORITY); - return queue; - } - - TaskQueueImpl::Task FakeTaskWithEnqueueOrder(int enqueue_order) { - TaskQueueImpl::Task fake_task(FROM_HERE, base::Closure(), base::TimeTicks(), - 0, true); - fake_task.set_enqueue_order(enqueue_order); - return fake_task; - } - - std::vector<std::unique_ptr<WorkQueue>> work_queues_; - std::unique_ptr<WorkQueueSets> work_queue_sets_; -}; - -TEST_F(WorkQueueSetsTest, ChangeSetIndex) { - WorkQueue* work_queue = NewTaskQueue("queue"); - size_t set = TaskQueue::NORMAL_PRIORITY; - work_queue_sets_->ChangeSetIndex(work_queue, set); - - EXPECT_EQ(set, work_queue->work_queue_set_index()); -} - -TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_QueueEmpty) { - WorkQueue* work_queue = NewTaskQueue("queue"); - size_t set = TaskQueue::NORMAL_PRIORITY; - work_queue_sets_->ChangeSetIndex(work_queue, set); - - WorkQueue* selected_work_queue; - EXPECT_FALSE( - work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); -} - -TEST_F(WorkQueueSetsTest, OnPushQueue) { - WorkQueue* work_queue = NewTaskQueue("queue"); - size_t set = TaskQueue::NORMAL_PRIORITY; - work_queue_sets_->ChangeSetIndex(work_queue, set); - - WorkQueue* selected_work_queue; - EXPECT_FALSE( - work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - - work_queue->Push(FakeTaskWithEnqueueOrder(10)); - work_queue_sets_->OnPushQueue(work_queue); - - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(work_queue, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_SingleTaskInSet) { - WorkQueue* work_queue = NewTaskQueue("queue"); - work_queue->Push(FakeTaskWithEnqueueOrder(10)); - size_t set = 1; - work_queue_sets_->ChangeSetIndex(work_queue, set); - - WorkQueue* selected_work_queue; - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(work_queue, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_MultipleAgesInSet) { - WorkQueue* queue1 = NewTaskQueue("queue1"); - WorkQueue* queue2 = NewTaskQueue("queue2"); - WorkQueue* queue3 = NewTaskQueue("queue2"); - queue1->Push(FakeTaskWithEnqueueOrder(6)); - queue2->Push(FakeTaskWithEnqueueOrder(5)); - queue3->Push(FakeTaskWithEnqueueOrder(4)); - size_t set = 2; - work_queue_sets_->ChangeSetIndex(queue1, set); - work_queue_sets_->ChangeSetIndex(queue2, set); - work_queue_sets_->ChangeSetIndex(queue3, set); - - WorkQueue* selected_work_queue; - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue3, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, OnPopQueue) { - WorkQueue* queue1 = NewTaskQueue("queue1"); - WorkQueue* queue2 = NewTaskQueue("queue2"); - WorkQueue* queue3 = NewTaskQueue("queue3"); - queue1->Push(FakeTaskWithEnqueueOrder(6)); - queue2->Push(FakeTaskWithEnqueueOrder(3)); - queue2->Push(FakeTaskWithEnqueueOrder(1)); - queue3->Push(FakeTaskWithEnqueueOrder(4)); - size_t set = 3; - work_queue_sets_->ChangeSetIndex(queue1, set); - work_queue_sets_->ChangeSetIndex(queue2, set); - work_queue_sets_->ChangeSetIndex(queue3, set); - - WorkQueue* selected_work_queue; - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue2, selected_work_queue); - - queue2->PopTaskForTest(); - work_queue_sets_->OnPopQueue(queue2); - - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue2, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, OnPopQueue_QueueBecomesEmpty) { - WorkQueue* queue1 = NewTaskQueue("queue"); - WorkQueue* queue2 = NewTaskQueue("queue"); - WorkQueue* queue3 = NewTaskQueue("queue"); - queue1->Push(FakeTaskWithEnqueueOrder(6)); - queue2->Push(FakeTaskWithEnqueueOrder(5)); - queue3->Push(FakeTaskWithEnqueueOrder(4)); - size_t set = 4; - work_queue_sets_->ChangeSetIndex(queue1, set); - work_queue_sets_->ChangeSetIndex(queue2, set); - work_queue_sets_->ChangeSetIndex(queue3, set); - - WorkQueue* selected_work_queue; - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue3, selected_work_queue); - - queue3->PopTaskForTest(); - work_queue_sets_->OnPopQueue(queue3); - - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue2, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, - GetOldestQueueInSet_MultipleAgesInSetIntegerRollover) { - WorkQueue* queue1 = NewTaskQueue("queue1"); - WorkQueue* queue2 = NewTaskQueue("queue2"); - WorkQueue* queue3 = NewTaskQueue("queue3"); - queue1->Push(FakeTaskWithEnqueueOrder(0x7ffffff1)); - queue2->Push(FakeTaskWithEnqueueOrder(0x7ffffff0)); - queue3->Push(FakeTaskWithEnqueueOrder(-0x7ffffff1)); - size_t set = 1; - work_queue_sets_->ChangeSetIndex(queue1, set); - work_queue_sets_->ChangeSetIndex(queue2, set); - work_queue_sets_->ChangeSetIndex(queue3, set); - - WorkQueue* selected_work_queue; - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue2, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_MultipleAgesInSet_RemoveQueue) { - WorkQueue* queue1 = NewTaskQueue("queue1"); - WorkQueue* queue2 = NewTaskQueue("queue2"); - WorkQueue* queue3 = NewTaskQueue("queue3"); - queue1->Push(FakeTaskWithEnqueueOrder(6)); - queue2->Push(FakeTaskWithEnqueueOrder(5)); - queue3->Push(FakeTaskWithEnqueueOrder(4)); - size_t set = 1; - work_queue_sets_->ChangeSetIndex(queue1, set); - work_queue_sets_->ChangeSetIndex(queue2, set); - work_queue_sets_->ChangeSetIndex(queue3, set); - work_queue_sets_->RemoveQueue(queue3); - - WorkQueue* selected_work_queue; - EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue)); - EXPECT_EQ(queue2, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, ChangeSetIndex_Complex) { - WorkQueue* queue1 = NewTaskQueue("queue1"); - WorkQueue* queue2 = NewTaskQueue("queue2"); - WorkQueue* queue3 = NewTaskQueue("queue3"); - WorkQueue* queue4 = NewTaskQueue("queue4"); - queue1->Push(FakeTaskWithEnqueueOrder(6)); - queue2->Push(FakeTaskWithEnqueueOrder(5)); - queue3->Push(FakeTaskWithEnqueueOrder(4)); - queue4->Push(FakeTaskWithEnqueueOrder(3)); - size_t set1 = 1; - size_t set2 = 2; - work_queue_sets_->ChangeSetIndex(queue1, set1); - work_queue_sets_->ChangeSetIndex(queue2, set1); - work_queue_sets_->ChangeSetIndex(queue3, set2); - work_queue_sets_->ChangeSetIndex(queue4, set2); - - WorkQueue* selected_work_queue; - EXPECT_TRUE( - work_queue_sets_->GetOldestQueueInSet(set1, &selected_work_queue)); - EXPECT_EQ(queue2, selected_work_queue); - - EXPECT_TRUE( - work_queue_sets_->GetOldestQueueInSet(set2, &selected_work_queue)); - EXPECT_EQ(queue4, selected_work_queue); - - work_queue_sets_->ChangeSetIndex(queue4, set1); - - EXPECT_TRUE( - work_queue_sets_->GetOldestQueueInSet(set1, &selected_work_queue)); - EXPECT_EQ(queue4, selected_work_queue); - - EXPECT_TRUE( - work_queue_sets_->GetOldestQueueInSet(set2, &selected_work_queue)); - EXPECT_EQ(queue3, selected_work_queue); -} - -TEST_F(WorkQueueSetsTest, IsSetEmpty_NoWork) { - size_t set = 2; - EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set)); - - WorkQueue* work_queue = NewTaskQueue("queue"); - work_queue_sets_->ChangeSetIndex(work_queue, set); - EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set)); -} - -TEST_F(WorkQueueSetsTest, IsSetEmpty_Work) { - size_t set = 2; - EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set)); - - WorkQueue* work_queue = NewTaskQueue("queue"); - work_queue->Push(FakeTaskWithEnqueueOrder(1)); - work_queue_sets_->ChangeSetIndex(work_queue, set); - EXPECT_FALSE(work_queue_sets_->IsSetEmpty(set)); - - work_queue->PopTaskForTest(); - work_queue_sets_->OnPopQueue(work_queue); - EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set)); -} - -} // namespace internal -} // namespace scheduler |