diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/base/task/sequence_manager | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base/task/sequence_manager')
24 files changed, 932 insertions, 238 deletions
diff --git a/chromium/base/task/sequence_manager/lazily_deallocated_deque.h b/chromium/base/task/sequence_manager/lazily_deallocated_deque.h index b7d1b428afa..e439b73c457 100644 --- a/chromium/base/task/sequence_manager/lazily_deallocated_deque.h +++ b/chromium/base/task/sequence_manager/lazily_deallocated_deque.h @@ -10,9 +10,9 @@ #include <memory> #include <vector> +#include "base/check_op.h" #include "base/debug/alias.h" #include "base/gtest_prod_util.h" -#include "base/logging.h" #include "base/macros.h" #include "base/time/time.h" diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.cc b/chromium/base/task/sequence_manager/sequence_manager_impl.cc index 3262cadd9a3..31db9535321 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_impl.cc @@ -13,6 +13,7 @@ #include "base/debug/crash_logging.h" #include "base/debug/stack_trace.h" #include "base/json/json_writer.h" +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop_current.h" #include "base/no_destructor.h" @@ -28,7 +29,7 @@ #include "base/threading/thread_local.h" #include "base/time/default_tick_clock.h" #include "base/time/tick_clock.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" #include "build/build_config.h" namespace base { @@ -42,6 +43,25 @@ GetTLSSequenceManagerImpl() { return lazy_tls_ptr.get(); } +class TracedBaseValue : public trace_event::ConvertableToTraceFormat { + public: + explicit TracedBaseValue(Value value) : value_(std::move(value)) {} + ~TracedBaseValue() override = default; + + void AppendAsTraceFormat(std::string* out) const override { + if (!value_.is_none()) { + std::string tmp; + JSONWriter::Write(value_, &tmp); + *out += tmp; + } else { + *out += "{}"; + } + } + + private: + base::Value value_; +}; + } // namespace std::unique_ptr<SequenceManager> CreateSequenceManagerOnCurrentThread( @@ -484,8 +504,8 @@ const char* RunTaskTraceNameForPriority(TaskQueue::QueuePriority priority) { } // namespace -Task* SequenceManagerImpl::SelectNextTask() { - Task* task = SelectNextTaskImpl(); +Task* SequenceManagerImpl::SelectNextTask(SelectTaskOption option) { + Task* task = SelectNextTaskImpl(option); if (!task) return nullptr; @@ -557,7 +577,7 @@ void SequenceManagerImpl::LogTaskDebugInfo( } #endif // DCHECK_IS_ON() && !defined(OS_NACL) -Task* SequenceManagerImpl::SelectNextTaskImpl() { +Task* SequenceManagerImpl::SelectNextTaskImpl(SelectTaskOption option) { CHECK(Validate()); DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); @@ -577,10 +597,12 @@ Task* SequenceManagerImpl::SelectNextTaskImpl() { while (true) { internal::WorkQueue* work_queue = - main_thread_only().selector.SelectWorkQueueToService(); + main_thread_only().selector.SelectWorkQueueToService(option); TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( TRACE_DISABLED_BY_DEFAULT("sequence_manager.debug"), "SequenceManager", - this, AsValueWithSelectorResult(work_queue, /* force_verbose */ false)); + this, + AsValueWithSelectorResultForTracing(work_queue, + /* force_verbose */ false)); if (!work_queue) return nullptr; @@ -648,15 +670,18 @@ void SequenceManagerImpl::DidRunTask() { CleanUpQueues(); } -TimeDelta SequenceManagerImpl::DelayTillNextTask(LazyNow* lazy_now) const { +TimeDelta SequenceManagerImpl::DelayTillNextTask( + LazyNow* lazy_now, + SelectTaskOption option) const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); - if (auto priority = main_thread_only().selector.GetHighestPendingPriority()) { + if (auto priority = + main_thread_only().selector.GetHighestPendingPriority(option)) { // If the selector has non-empty queues we trivially know there is immediate // work to be done. However we may want to yield to native work if it is // more important. if (UNLIKELY(!ShouldRunTaskOfPriority(*priority))) - return GetDelayTillNextDelayedTask(lazy_now); + return GetDelayTillNextDelayedTask(lazy_now, option); return TimeDelta(); } @@ -664,9 +689,11 @@ TimeDelta SequenceManagerImpl::DelayTillNextTask(LazyNow* lazy_now) const { // NB ReloadEmptyWorkQueues involves a memory barrier, so it's fastest to not // do this always. ReloadEmptyWorkQueues(); - if (auto priority = main_thread_only().selector.GetHighestPendingPriority()) { + + if (auto priority = + main_thread_only().selector.GetHighestPendingPriority(option)) { if (UNLIKELY(!ShouldRunTaskOfPriority(*priority))) - return GetDelayTillNextDelayedTask(lazy_now); + return GetDelayTillNextDelayedTask(lazy_now, option); return TimeDelta(); } @@ -674,13 +701,17 @@ TimeDelta SequenceManagerImpl::DelayTillNextTask(LazyNow* lazy_now) const { // call MoveReadyDelayedTasksToWorkQueues because it's assumed // DelayTillNextTask will return TimeDelta>() if the delayed task is due to // run now. - return GetDelayTillNextDelayedTask(lazy_now); + return GetDelayTillNextDelayedTask(lazy_now, option); } TimeDelta SequenceManagerImpl::GetDelayTillNextDelayedTask( - LazyNow* lazy_now) const { + LazyNow* lazy_now, + SelectTaskOption option) const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); + if (option == SelectTaskOption::kSkipDelayedTask) + return TimeDelta::Max(); + TimeDelta delay_till_next_task = TimeDelta::Max(); for (TimeDomain* time_domain : main_thread_only().time_domains) { Optional<TimeDelta> delay = time_domain->DelayTillNextTask(lazy_now); @@ -895,49 +926,45 @@ EnqueueOrder SequenceManagerImpl::GetNextSequenceNumber() { } std::unique_ptr<trace_event::ConvertableToTraceFormat> -SequenceManagerImpl::AsValueWithSelectorResult( +SequenceManagerImpl::AsValueWithSelectorResultForTracing( internal::WorkQueue* selected_work_queue, bool force_verbose) const { - auto state = std::make_unique<trace_event::TracedValue>(); - AsValueWithSelectorResultInto(state.get(), selected_work_queue, - force_verbose); - return std::move(state); + return std::make_unique<TracedBaseValue>( + AsValueWithSelectorResult(selected_work_queue, force_verbose)); } -void SequenceManagerImpl::AsValueWithSelectorResultInto( - trace_event::TracedValue* state, +Value SequenceManagerImpl::AsValueWithSelectorResult( internal::WorkQueue* selected_work_queue, bool force_verbose) const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); TimeTicks now = NowTicks(); - state->BeginArray("active_queues"); + Value state(Value::Type::DICTIONARY); + Value active_queues(Value::Type::LIST); for (auto* const queue : main_thread_only().active_queues) - queue->AsValueInto(now, state, force_verbose); - state->EndArray(); - state->BeginArray("queues_to_gracefully_shutdown"); + active_queues.Append(queue->AsValue(now, force_verbose)); + state.SetKey("active_queues", std::move(active_queues)); + Value shutdown_queues(Value::Type::LIST); for (const auto& pair : main_thread_only().queues_to_gracefully_shutdown) - pair.first->AsValueInto(now, state, force_verbose); - state->EndArray(); - state->BeginArray("queues_to_delete"); + shutdown_queues.Append(pair.first->AsValue(now, force_verbose)); + state.SetKey("queues_to_gracefully_shutdown", std::move(shutdown_queues)); + Value queues_to_delete(Value::Type::LIST); for (const auto& pair : main_thread_only().queues_to_delete) - pair.first->AsValueInto(now, state, force_verbose); - state->EndArray(); - state->BeginDictionary("selector"); - main_thread_only().selector.AsValueInto(state); - state->EndDictionary(); + queues_to_delete.Append(pair.first->AsValue(now, force_verbose)); + state.SetKey("queues_to_delete", std::move(queues_to_delete)); + state.SetKey("selector", main_thread_only().selector.AsValue()); if (selected_work_queue) { - state->SetString("selected_queue", - selected_work_queue->task_queue()->GetName()); - state->SetString("work_queue_name", selected_work_queue->name()); + state.SetStringKey("selected_queue", + selected_work_queue->task_queue()->GetName()); + state.SetStringKey("work_queue_name", selected_work_queue->name()); } - state->SetString("native_work_priority", - TaskQueue::PriorityToString( - *main_thread_only().pending_native_work.begin())); - - state->BeginArray("time_domains"); + state.SetStringKey("native_work_priority", + TaskQueue::PriorityToString( + *main_thread_only().pending_native_work.begin())); + Value time_domains(Value::Type::LIST); for (auto* time_domain : main_thread_only().time_domains) - time_domain->AsValueInto(state); - state->EndArray(); + time_domains.Append(time_domain->AsValue()); + state.SetKey("time_domains", std::move(time_domains)); + return state; } void SequenceManagerImpl::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) { @@ -1092,9 +1119,10 @@ scoped_refptr<TaskQueue> SequenceManagerImpl::CreateTaskQueue( } std::string SequenceManagerImpl::DescribeAllPendingTasks() const { - trace_event::TracedValueJSON value; - AsValueWithSelectorResultInto(&value, nullptr, /* force_verbose */ true); - return value.ToJSON(); + Value value = AsValueWithSelectorResult(nullptr, /* force_verbose */ true); + std::string result; + JSONWriter::Write(value, &result); + return result; } std::unique_ptr<NativeWorkHandle> SequenceManagerImpl::OnNativeWorkPending( diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.h b/chromium/base/task/sequence_manager/sequence_manager_impl.h index cf22672ecca..10fd729b6db 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl.h +++ b/chromium/base/task/sequence_manager/sequence_manager_impl.h @@ -38,6 +38,7 @@ #include "base/task/sequence_manager/thread_controller.h" #include "base/threading/thread_checker.h" #include "base/time/default_tick_clock.h" +#include "base/values.h" #include "build/build_config.h" namespace base { @@ -125,9 +126,12 @@ class BASE_EXPORT SequenceManagerImpl void RemoveTaskObserver(TaskObserver* task_observer) override; // SequencedTaskSource implementation: - Task* SelectNextTask() override; + Task* SelectNextTask( + SelectTaskOption option = SelectTaskOption::kDefault) override; void DidRunTask() override; - TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override; + TimeDelta DelayTillNextTask( + LazyNow* lazy_now, + SelectTaskOption option = SelectTaskOption::kDefault) const override; bool HasPendingHighResolutionTasks() override; bool OnSystemIdle() override; @@ -342,11 +346,10 @@ class BASE_EXPORT SequenceManagerImpl bool GetAddQueueTimeToTasks(); std::unique_ptr<trace_event::ConvertableToTraceFormat> - AsValueWithSelectorResult(internal::WorkQueue* selected_work_queue, - bool force_verbose) const; - void AsValueWithSelectorResultInto(trace_event::TracedValue*, - internal::WorkQueue* selected_work_queue, - bool force_verbose) const; + AsValueWithSelectorResultForTracing(internal::WorkQueue* selected_work_queue, + bool force_verbose) const; + Value AsValueWithSelectorResult(internal::WorkQueue* selected_work_queue, + bool force_verbose) const; // Used in construction of TaskQueueImpl to obtain an AtomicFlag which it can // use to request reload by ReloadEmptyWorkQueues. The lifetime of @@ -379,14 +382,15 @@ class BASE_EXPORT SequenceManagerImpl // Helper to terminate all scoped trace events to allow starting new ones // in SelectNextTask(). - Task* SelectNextTaskImpl(); + Task* SelectNextTaskImpl(SelectTaskOption option); // Check if a task of priority |priority| should run given the pending set of // native work. bool ShouldRunTaskOfPriority(TaskQueue::QueuePriority priority) const; // Ignores any immediate work. - TimeDelta GetDelayTillNextDelayedTask(LazyNow* lazy_now) const; + TimeDelta GetDelayTillNextDelayedTask(LazyNow* lazy_now, + SelectTaskOption option) const; #if DCHECK_IS_ON() void LogTaskDebugInfo(const internal::WorkQueue* work_queue) const; diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc index 584691fe3b0..baf3d6fbdb9 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc @@ -48,14 +48,18 @@ #include "base/test/task_environment.h" #include "base/test/test_mock_time_task_runner.h" #include "base/test/test_simple_task_runner.h" -#include "base/test/trace_event_analyzer.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "base/trace_event/blame_context.h" +#include "base/trace_event/base_tracing.h" +#include "base/tracing_buildflags.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" +#if BUILDFLAG(ENABLE_BASE_TRACING) +#include "base/test/trace_event_analyzer.h" +#endif // BUILDFLAG(ENABLE_BASE_TRACING) + using base::sequence_manager::EnqueueOrder; using testing::_; using testing::AnyNumber; @@ -2719,6 +2723,7 @@ TEST_P(SequenceManagerTest, CurrentlyExecutingTaskQueue_NestedLoop) { EXPECT_EQ(nullptr, sequence_manager()->currently_executing_task_queue()); } +#if BUILDFLAG(ENABLE_BASE_TRACING) TEST_P(SequenceManagerTest, BlameContextAttribution) { if (GetUnderlyingRunnerType() == TestType::kMessagePump) return; @@ -2744,6 +2749,7 @@ TEST_P(SequenceManagerTest, BlameContextAttribution) { EXPECT_EQ(2u, events.size()); } +#endif // BUILDFLAG(ENABLE_BASE_TRACING) TEST_P(SequenceManagerTest, NoWakeUpsForCanceledDelayedTasks) { auto queue = CreateTaskQueue(); @@ -2993,6 +2999,181 @@ TEST_P(SequenceManagerTest, SweepCanceledDelayedTasks_ManyTasks) { } } +TEST_P(SequenceManagerTest, DelayedTasksNotSelected) { + auto queue = CreateTaskQueue(); + constexpr TimeDelta kDelay(TimeDelta::FromMilliseconds(10)); + LazyNow lazy_now(mock_tick_clock()); + EXPECT_EQ(TimeDelta::Max(), sequence_manager()->DelayTillNextTask(&lazy_now)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), kDelay); + + // No task should be ready to execute. + EXPECT_FALSE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kDefault)); + EXPECT_FALSE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + EXPECT_EQ(kDelay, sequence_manager()->DelayTillNextTask(&lazy_now)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + AdvanceMockTickClock(kDelay); + LazyNow lazy_now2(mock_tick_clock()); + + // Delayed task is ready to be executed. Consider it only if not in power + // suspend state. + EXPECT_FALSE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now2, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + // Execute the delayed task. + EXPECT_TRUE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kDefault)); + sequence_manager()->DidRunTask(); + EXPECT_EQ(TimeDelta::Max(), + sequence_manager()->DelayTillNextTask(&lazy_now2)); + + // Tidy up. + queue->ShutdownTaskQueue(); +} + +TEST_P(SequenceManagerTest, DelayedTasksNotSelectedWithImmediateTask) { + auto queue = CreateTaskQueue(); + constexpr TimeDelta kDelay(TimeDelta::FromMilliseconds(10)); + LazyNow lazy_now(mock_tick_clock()); + + EXPECT_EQ(TimeDelta::Max(), sequence_manager()->DelayTillNextTask(&lazy_now)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + // Post an immediate task. + queue->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask)); + queue->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), kDelay); + + EXPECT_EQ(TimeDelta(), sequence_manager()->DelayTillNextTask(&lazy_now)); + EXPECT_EQ( + TimeDelta(), + sequence_manager()->DelayTillNextTask( + &lazy_now, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + AdvanceMockTickClock(kDelay); + LazyNow lazy_now2(mock_tick_clock()); + + // An immediate task is present, even if we skip the delayed tasks. + EXPECT_EQ( + TimeDelta(), + sequence_manager()->DelayTillNextTask( + &lazy_now2, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + // Immediate task should be ready to execute, execute it. + EXPECT_TRUE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + sequence_manager()->DidRunTask(); + + // Delayed task is ready to be executed. Consider it only if not in power + // suspend state. This test differs from + // SequenceManagerTest.DelayedTasksNotSelected as it confirms that delayed + // tasks are ignored even if they're already in the ready queue (per having + // performed task selection already before running the immediate task above). + EXPECT_FALSE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now2, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + // Execute the delayed task. + EXPECT_TRUE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kDefault)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now2, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + sequence_manager()->DidRunTask(); + + // Tidy up. + queue->ShutdownTaskQueue(); +} + +TEST_P(SequenceManagerTest, + DelayedTasksNotSelectedWithImmediateTaskWithPriority) { + auto queues = CreateTaskQueues(4u); + queues[0]->SetQueuePriority(TaskQueue::QueuePriority::kLowPriority); + queues[1]->SetQueuePriority(TaskQueue::QueuePriority::kNormalPriority); + queues[2]->SetQueuePriority(TaskQueue::QueuePriority::kHighPriority); + queues[3]->SetQueuePriority(TaskQueue::QueuePriority::kVeryHighPriority); + + // Post immediate tasks. + queues[0]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask)); + queues[2]->task_runner()->PostTask(FROM_HERE, BindOnce(&NopTask)); + + // Post delayed tasks. + constexpr TimeDelta kDelay(TimeDelta::FromMilliseconds(10)); + queues[1]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), + kDelay); + queues[3]->task_runner()->PostDelayedTask(FROM_HERE, BindOnce(&NopTask), + kDelay); + + LazyNow lazy_now(mock_tick_clock()); + + EXPECT_EQ( + TimeDelta(), + sequence_manager()->DelayTillNextTask( + &lazy_now, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + AdvanceMockTickClock(kDelay); + LazyNow lazy_now2(mock_tick_clock()); + + EXPECT_EQ( + TimeDelta(), + sequence_manager()->DelayTillNextTask( + &lazy_now2, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + // Immediate tasks should be ready to execute, execute them. + EXPECT_TRUE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + sequence_manager()->DidRunTask(); + EXPECT_TRUE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + sequence_manager()->DidRunTask(); + + // No immediate tasks can be executed anymore. + EXPECT_FALSE(sequence_manager()->SelectNextTask( + SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + EXPECT_EQ( + TimeDelta::Max(), + sequence_manager()->DelayTillNextTask( + &lazy_now2, SequencedTaskSource::SelectTaskOption::kSkipDelayedTask)); + + // Execute delayed tasks. + EXPECT_TRUE(sequence_manager()->SelectNextTask()); + sequence_manager()->DidRunTask(); + EXPECT_TRUE(sequence_manager()->SelectNextTask()); + sequence_manager()->DidRunTask(); + + // No delayed tasks can be executed anymore. + EXPECT_FALSE(sequence_manager()->SelectNextTask()); + EXPECT_EQ(TimeDelta::Max(), + sequence_manager()->DelayTillNextTask(&lazy_now2)); + + // Tidy up. + queues[0]->ShutdownTaskQueue(); + queues[1]->ShutdownTaskQueue(); + queues[2]->ShutdownTaskQueue(); + queues[3]->ShutdownTaskQueue(); +} + TEST_P(SequenceManagerTest, DelayTillNextTask) { auto queues = CreateTaskQueues(2u); @@ -4159,8 +4340,6 @@ class MockTimeDomain : public TimeDomain { MOCK_METHOD1(MaybeFastForwardToNextTask, bool(bool quit_when_idle_requested)); - void AsValueIntoInternal(trace_event::TracedValue* state) const override {} - const char* GetName() const override { return "Test"; } void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override {} diff --git a/chromium/base/task/sequence_manager/sequence_manager_perftest.cc b/chromium/base/task/sequence_manager/sequence_manager_perftest.cc index 463f82bf2f3..5ea530c2cce 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_perftest.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_perftest.cc @@ -8,6 +8,7 @@ #include <memory> #include "base/bind.h" +#include "base/logging.h" #include "base/message_loop/message_pump_default.h" #include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" diff --git a/chromium/base/task/sequence_manager/sequenced_task_source.h b/chromium/base/task/sequence_manager/sequenced_task_source.h index 5ea8874ab5e..7fea4d213b3 100644 --- a/chromium/base/task/sequence_manager/sequenced_task_source.h +++ b/chromium/base/task/sequence_manager/sequenced_task_source.h @@ -17,20 +17,27 @@ namespace internal { // Interface to pass tasks to ThreadController. class SequencedTaskSource { public: + enum class SelectTaskOption { kDefault, kSkipDelayedTask }; + virtual ~SequencedTaskSource() = default; // Returns the next task to run from this source or nullptr if // there're no more tasks ready to run. If a task is returned, // DidRunTask() must be invoked before the next call to SelectNextTask(). - virtual Task* SelectNextTask() = 0; + // |option| allows control on which kind of tasks can be selected. + virtual Task* SelectNextTask( + SelectTaskOption option = SelectTaskOption::kDefault) = 0; // Notifies this source that the task previously obtained // from SelectNextTask() has been completed. virtual void DidRunTask() = 0; // Returns the delay till the next task or TimeDelta::Max() - // if there are no tasks left. - virtual TimeDelta DelayTillNextTask(LazyNow* lazy_now) const = 0; + // if there are no tasks left. |option| allows control on which kind of tasks + // can be selected. + virtual TimeDelta DelayTillNextTask( + LazyNow* lazy_now, + SelectTaskOption option = SelectTaskOption::kDefault) const = 0; // Return true if there are any pending tasks in the task source which require // high resolution timing. diff --git a/chromium/base/task/sequence_manager/task_queue_impl.cc b/chromium/base/task/sequence_manager/task_queue_impl.cc index 88305f84345..2a71aabf793 100644 --- a/chromium/base/task/sequence_manager/task_queue_impl.cc +++ b/chromium/base/task/sequence_manager/task_queue_impl.cc @@ -4,9 +4,12 @@ #include "base/task/sequence_manager/task_queue_impl.h" +#include <inttypes.h> + #include <memory> #include <utility> +#include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/task/common/scoped_defer_task_posting.h" #include "base/task/sequence_manager/sequence_manager_impl.h" @@ -15,8 +18,7 @@ #include "base/task/task_observer.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" -#include "base/trace_event/blame_context.h" -#include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/base_tracing.h" #include "build/build_config.h" namespace base { @@ -650,55 +652,52 @@ TaskQueue::QueuePriority TaskQueueImpl::GetQueuePriority() const { return static_cast<TaskQueue::QueuePriority>(set_index); } -void TaskQueueImpl::AsValueInto(TimeTicks now, - trace_event::TracedValue* state, - bool force_verbose) const { +Value TaskQueueImpl::AsValue(TimeTicks now, bool force_verbose) const { base::internal::CheckedAutoLock lock(any_thread_lock_); - state->BeginDictionary(); - state->SetString("name", GetName()); + Value state(Value::Type::DICTIONARY); + state.SetStringKey("name", GetName()); if (any_thread_.unregistered) { - state->SetBoolean("unregistered", true); - state->EndDictionary(); - return; + state.SetBoolKey("unregistered", true); + return state; } DCHECK(main_thread_only().time_domain); DCHECK(main_thread_only().delayed_work_queue); DCHECK(main_thread_only().immediate_work_queue); - state->SetString( + state.SetStringKey( "task_queue_id", StringPrintf("0x%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)))); - state->SetBoolean("enabled", IsQueueEnabled()); - state->SetString("time_domain_name", - main_thread_only().time_domain->GetName()); - state->SetInteger("any_thread_.immediate_incoming_queuesize", - 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()); - - state->SetInteger("any_thread_.immediate_incoming_queuecapacity", - any_thread_.immediate_incoming_queue.capacity()); - state->SetInteger("immediate_work_queue_capacity", - immediate_work_queue()->Capacity()); - state->SetInteger("delayed_work_queue_capacity", - delayed_work_queue()->Capacity()); + state.SetBoolKey("enabled", IsQueueEnabled()); + state.SetStringKey("time_domain_name", + main_thread_only().time_domain->GetName()); + state.SetIntKey("any_thread_.immediate_incoming_queuesize", + any_thread_.immediate_incoming_queue.size()); + state.SetIntKey("delayed_incoming_queue_size", + main_thread_only().delayed_incoming_queue.size()); + state.SetIntKey("immediate_work_queue_size", + main_thread_only().immediate_work_queue->Size()); + state.SetIntKey("delayed_work_queue_size", + main_thread_only().delayed_work_queue->Size()); + + state.SetIntKey("any_thread_.immediate_incoming_queuecapacity", + any_thread_.immediate_incoming_queue.capacity()); + state.SetIntKey("immediate_work_queue_capacity", + immediate_work_queue()->Capacity()); + state.SetIntKey("delayed_work_queue_capacity", + delayed_work_queue()->Capacity()); if (!main_thread_only().delayed_incoming_queue.empty()) { 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()); + state.SetDoubleKey("delay_to_next_task_ms", + delay_to_next_task.InMillisecondsF()); } if (main_thread_only().current_fence) - state->SetInteger("current_fence", main_thread_only().current_fence); + state.SetIntKey("current_fence", main_thread_only().current_fence); if (main_thread_only().delayed_fence) { - state->SetDouble( + state.SetDoubleKey( "delayed_fence_seconds_from_now", (main_thread_only().delayed_fence.value() - now).InSecondsF()); } @@ -709,21 +708,18 @@ void TaskQueueImpl::AsValueInto(TimeTicks now, &verbose); if (verbose || force_verbose) { - state->BeginArray("immediate_incoming_queue"); - QueueAsValueInto(any_thread_.immediate_incoming_queue, now, state); - state->EndArray(); - state->BeginArray("delayed_work_queue"); - main_thread_only().delayed_work_queue->AsValueInto(now, state); - state->EndArray(); - state->BeginArray("immediate_work_queue"); - main_thread_only().immediate_work_queue->AsValueInto(now, state); - state->EndArray(); - state->BeginArray("delayed_incoming_queue"); - main_thread_only().delayed_incoming_queue.AsValueInto(now, state); - state->EndArray(); + state.SetKey("immediate_incoming_queue", + QueueAsValue(any_thread_.immediate_incoming_queue, now)); + state.SetKey("delayed_work_queue", + main_thread_only().delayed_work_queue->AsValue(now)); + state.SetKey("immediate_work_queue", + main_thread_only().immediate_work_queue->AsValue(now)); + state.SetKey("delayed_incoming_queue", + main_thread_only().delayed_incoming_queue.AsValue(now)); } - state->SetString("priority", TaskQueue::PriorityToString(GetQueuePriority())); - state->EndDictionary(); + state.SetStringKey("priority", + TaskQueue::PriorityToString(GetQueuePriority())); + return state; } void TaskQueueImpl::AddTaskObserver(TaskObserver* task_observer) { @@ -913,34 +909,31 @@ bool TaskQueueImpl::WasBlockedOrLowPriority(EnqueueOrder enqueue_order) const { } // static -void TaskQueueImpl::QueueAsValueInto(const TaskDeque& queue, - TimeTicks now, - trace_event::TracedValue* state) { - for (const Task& task : queue) { - TaskAsValueInto(task, now, state); - } +Value TaskQueueImpl::QueueAsValue(const TaskDeque& queue, TimeTicks now) { + Value state(Value::Type::LIST); + for (const Task& task : queue) + state.Append(TaskAsValue(task, now)); + return state; } // static -void TaskQueueImpl::TaskAsValueInto(const Task& task, - TimeTicks now, - trace_event::TracedValue* state) { - state->BeginDictionary(); - state->SetString("posted_from", task.posted_from.ToString()); +Value TaskQueueImpl::TaskAsValue(const Task& task, TimeTicks now) { + Value state(Value::Type::DICTIONARY); + state.SetStringKey("posted_from", task.posted_from.ToString()); if (task.enqueue_order_set()) - state->SetInteger("enqueue_order", task.enqueue_order()); - state->SetInteger("sequence_num", task.sequence_num); - state->SetBoolean("nestable", task.nestable == Nestable::kNestable); - state->SetBoolean("is_high_res", task.is_high_res); - state->SetBoolean("is_cancelled", task.task.IsCancelled()); - state->SetDouble("delayed_run_time", - (task.delayed_run_time - TimeTicks()).InMillisecondsF()); + state.SetIntKey("enqueue_order", task.enqueue_order()); + state.SetIntKey("sequence_num", task.sequence_num); + state.SetBoolKey("nestable", task.nestable == Nestable::kNestable); + state.SetBoolKey("is_high_res", task.is_high_res); + state.SetBoolKey("is_cancelled", task.task.IsCancelled()); + state.SetDoubleKey("delayed_run_time", + (task.delayed_run_time - TimeTicks()).InMillisecondsF()); const TimeDelta delayed_run_time_milliseconds_from_now = task.delayed_run_time.is_null() ? TimeDelta() : (task.delayed_run_time - now); - state->SetDouble("delayed_run_time_milliseconds_from_now", - delayed_run_time_milliseconds_from_now.InMillisecondsF()); - state->EndDictionary(); + state.SetDoubleKey("delayed_run_time_milliseconds_from_now", + delayed_run_time_milliseconds_from_now.InMillisecondsF()); + return state; } bool TaskQueueImpl::IsQueueEnabled() const { @@ -1426,12 +1419,11 @@ void TaskQueueImpl::DelayedIncomingQueue::SweepCancelledTasks() { std::make_heap(queue_.c.begin(), queue_.c.end(), queue_.comp); } -void TaskQueueImpl::DelayedIncomingQueue::AsValueInto( - TimeTicks now, - trace_event::TracedValue* state) const { - for (const Task& task : queue_.c) { - TaskAsValueInto(task, now, state); - } +Value TaskQueueImpl::DelayedIncomingQueue::AsValue(TimeTicks now) const { + Value state(Value::Type::LIST); + for (const Task& task : queue_.c) + state.Append(TaskAsValue(task, now)); + return state; } } // namespace internal diff --git a/chromium/base/task/sequence_manager/task_queue_impl.h b/chromium/base/task/sequence_manager/task_queue_impl.h index aa382fb1490..b781bdb2b33 100644 --- a/chromium/base/task/sequence_manager/task_queue_impl.h +++ b/chromium/base/task/sequence_manager/task_queue_impl.h @@ -14,6 +14,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/observer_list.h" #include "base/pending_task.h" #include "base/task/common/checked_lock.h" #include "base/task/common/intrusive_heap.h" @@ -25,8 +26,9 @@ #include "base/task/sequence_manager/sequenced_task_source.h" #include "base/task/sequence_manager/task_queue.h" #include "base/threading/thread_checker.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/traced_value.h" +#include "base/time/time_override.h" +#include "base/trace_event/base_tracing.h" +#include "base/values.h" namespace base { namespace sequence_manager { @@ -141,9 +143,7 @@ class BASE_EXPORT TaskQueueImpl { // Must only be called from the thread this task queue was created on. void ReloadEmptyImmediateWorkQueue(); - void AsValueInto(TimeTicks now, - trace_event::TracedValue* state, - bool force_verbose) const; + Value AsValue(TimeTicks now, bool force_verbose) const; bool GetQuiescenceMonitored() const { return should_monitor_quiescence_; } bool GetShouldNotifyObservers() const { return should_notify_observers_; } @@ -322,7 +322,7 @@ class BASE_EXPORT TaskQueueImpl { void SweepCancelledTasks(); std::priority_queue<Task> TakeTasks() { return std::move(queue_); } - void AsValueInto(TimeTicks now, trace_event::TracedValue* state) const; + Value AsValue(TimeTicks now) const; private: struct PQueue : public std::priority_queue<Task> { @@ -428,15 +428,8 @@ class BASE_EXPORT TaskQueueImpl { void TakeImmediateIncomingQueueTasks(TaskDeque* queue); void TraceQueueSize() const; - static void QueueAsValueInto(const TaskDeque& queue, - TimeTicks now, - trace_event::TracedValue* state); - static void QueueAsValueInto(const std::priority_queue<Task>& queue, - TimeTicks now, - trace_event::TracedValue* state); - static void TaskAsValueInto(const Task& task, - TimeTicks now, - trace_event::TracedValue* state); + static Value QueueAsValue(const TaskDeque& queue, TimeTicks now); + static Value TaskAsValue(const Task& task, TimeTicks now); // Schedules delayed work on time domain and calls the observer. void UpdateDelayedWakeUp(LazyNow* lazy_now); diff --git a/chromium/base/task/sequence_manager/task_queue_selector.cc b/chromium/base/task/sequence_manager/task_queue_selector.cc index 3b4f59d1efc..3bacdc49e08 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector.cc +++ b/chromium/base/task/sequence_manager/task_queue_selector.cc @@ -12,7 +12,7 @@ #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/work_queue.h" #include "base/threading/thread_checker.h" -#include "base/trace_event/traced_value.h" +#include "base/trace_event/base_tracing.h" namespace base { namespace sequence_manager { @@ -167,17 +167,34 @@ bool TaskQueueSelector::CheckContainsQueueForTest( } #endif -WorkQueue* TaskQueueSelector::SelectWorkQueueToService() { +WorkQueue* TaskQueueSelector::SelectWorkQueueToService( + SelectTaskOption option) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); - if (!active_priority_tracker_.HasActivePriority()) + auto highest_priority = GetHighestPendingPriority(option); + if (!highest_priority.has_value()) return nullptr; // Select the priority from which we will select a task. Usually this is // the highest priority for which we have work, unless we are starving a lower // priority. - TaskQueue::QueuePriority priority = - active_priority_tracker_.HighestActivePriority(); + TaskQueue::QueuePriority priority = highest_priority.value(); + + // For selecting an immediate queue only, the highest priority can be used as + // a starting priority, but it is required to check work at other priorities. + // For the case where a delayed task is at a higher priority than an immediate + // task, HighestActivePriority(...) returns the priority of the delayed task + // but the resulting queue must be the lower one. + if (option == SelectTaskOption::kSkipDelayedTask) { + WorkQueue* queue = +#if DCHECK_IS_ON() + random_task_selection_ + ? ChooseImmediateOnlyWithPriority<SetOperationRandom>(priority) + : +#endif + ChooseImmediateOnlyWithPriority<SetOperationOldest>(priority); + return queue; + } WorkQueue* queue = #if DCHECK_IS_ON() @@ -197,21 +214,37 @@ WorkQueue* TaskQueueSelector::SelectWorkQueueToService() { return queue; } -void TaskQueueSelector::AsValueInto(trace_event::TracedValue* state) const { +Value TaskQueueSelector::AsValue() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); - state->SetInteger("immediate_starvation_count", immediate_starvation_count_); + Value state(Value::Type::DICTIONARY); + state.SetIntKey("immediate_starvation_count", immediate_starvation_count_); + return state; } void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) { task_queue_selector_observer_ = observer; } -Optional<TaskQueue::QueuePriority> -TaskQueueSelector::GetHighestPendingPriority() const { +Optional<TaskQueue::QueuePriority> TaskQueueSelector::GetHighestPendingPriority( + SelectTaskOption option) const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); if (!active_priority_tracker_.HasActivePriority()) return nullopt; - return active_priority_tracker_.HighestActivePriority(); + + TaskQueue::QueuePriority highest_priority = + active_priority_tracker_.HighestActivePriority(); + if (option != SelectTaskOption::kSkipDelayedTask) + return highest_priority; + + for (; highest_priority != TaskQueue::kQueuePriorityCount; + highest_priority = NextPriority(highest_priority)) { + if (active_priority_tracker_.IsActive(highest_priority) && + !immediate_work_queue_sets_.IsSetEmpty(highest_priority)) { + return highest_priority; + } + } + + return nullopt; } void TaskQueueSelector::SetImmediateStarvationCountForTest( @@ -220,7 +253,7 @@ void TaskQueueSelector::SetImmediateStarvationCountForTest( } bool TaskQueueSelector::HasTasksWithPriority( - TaskQueue::QueuePriority priority) { + TaskQueue::QueuePriority priority) const { return !delayed_work_queue_sets_.IsSetEmpty(priority) || !immediate_work_queue_sets_.IsSetEmpty(priority); } diff --git a/chromium/base/task/sequence_manager/task_queue_selector.h b/chromium/base/task/sequence_manager/task_queue_selector.h index 9df9ac8a32a..5ad4d8f462e 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector.h +++ b/chromium/base/task/sequence_manager/task_queue_selector.h @@ -11,8 +11,10 @@ #include "base/macros.h" #include "base/pending_task.h" #include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/sequenced_task_source.h" #include "base/task/sequence_manager/task_queue_selector_logic.h" #include "base/task/sequence_manager/work_queue_sets.h" +#include "base/values.h" namespace base { namespace sequence_manager { @@ -24,6 +26,8 @@ class AssociatedThreadId; // of particular task queues. class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { public: + using SelectTaskOption = SequencedTaskSource::SelectTaskOption; + TaskQueueSelector(scoped_refptr<AssociatedThreadId> associated_thread, const SequenceManager::Settings& settings); @@ -51,10 +55,11 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { // Called to choose the work queue from which the next task should be taken // and run. Return the queue to service if there is one or null otherwise. // This function is called on the main thread. - WorkQueue* SelectWorkQueueToService(); + WorkQueue* SelectWorkQueueToService( + SelectTaskOption option = SelectTaskOption::kDefault); - // Serialize the selector state for tracing. - void AsValueInto(trace_event::TracedValue* state) const; + // Serialize the selector state for tracing/debugging. + Value AsValue() const; class BASE_EXPORT Observer { public: @@ -70,7 +75,8 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { // Returns the priority of the most important pending task if one exists. // O(1). - Optional<TaskQueue::QueuePriority> GetHighestPendingPriority() const; + Optional<TaskQueue::QueuePriority> GetHighestPendingPriority( + SelectTaskOption option = SelectTaskOption::kDefault) const; // WorkQueueSets::Observer implementation: void WorkQueueSetBecameEmpty(size_t set_index) override; @@ -172,7 +178,7 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { // Select an immediate work queue if we are starving immediate tasks. if (immediate_starvation_count_ >= kMaxDelayedStarvationTasks) { WorkQueue* queue = - SetOperation::GetWithPriority(immediate_work_queue_sets_, priority); + ChooseImmediateOnlyWithPriority<SetOperation>(priority); if (queue) return queue; return SetOperation::GetWithPriority(delayed_work_queue_sets_, priority); @@ -180,6 +186,12 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { return ChooseImmediateOrDelayedTaskWithPriority<SetOperation>(priority); } + template <typename SetOperation> + WorkQueue* ChooseImmediateOnlyWithPriority( + TaskQueue::QueuePriority priority) const { + return SetOperation::GetWithPriority(immediate_work_queue_sets_, priority); + } + private: void ChangeSetIndex(internal::TaskQueueImpl* queue, TaskQueue::QueuePriority priority); @@ -218,7 +230,7 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { TaskQueue::QueuePriority priority); // Returns true if there are pending tasks with priority |priority|. - bool HasTasksWithPriority(TaskQueue::QueuePriority priority); + bool HasTasksWithPriority(TaskQueue::QueuePriority priority) const; scoped_refptr<AssociatedThreadId> associated_thread_; diff --git a/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc b/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc index 1ec6bdc3795..90413d29080 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc +++ b/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc @@ -396,6 +396,75 @@ TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyImmediate) { TaskQueue::kNormalPriority)); } +TEST_F(TaskQueueSelectorTest, + SelectWorkQueueToServiceImmediateOnlyWithoutImmediateTask) { + task_queues_[0]->delayed_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); + + EXPECT_EQ(nullptr, + selector_.SelectWorkQueueToService( + TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); + EXPECT_EQ(task_queues_[0]->delayed_work_queue(), + selector_.SelectWorkQueueToService()); +} + +TEST_F(TaskQueueSelectorTest, + SelectWorkQueueToServiceImmediateOnlyWithDelayedTasks) { + task_queues_[0]->delayed_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(1))); + task_queues_[0]->immediate_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); + + EXPECT_EQ(task_queues_[0]->immediate_work_queue(), + selector_.SelectWorkQueueToService( + TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); + EXPECT_EQ(task_queues_[0]->delayed_work_queue(), + selector_.SelectWorkQueueToService()); +} + +TEST_F(TaskQueueSelectorTest, + SelectWorkQueueToServiceImmediateOnlyWithDisabledQueues) { + task_queues_[0]->delayed_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(1))); + task_queues_[0]->immediate_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); + task_queues_[1]->delayed_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(3))); + task_queues_[2]->immediate_work_queue()->Push( + Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(), + EnqueueOrder(), EnqueueOrder::FromIntForTesting(4))); + + EXPECT_EQ(task_queues_[0]->delayed_work_queue(), + selector_.SelectWorkQueueToService()); + EXPECT_EQ(task_queues_[0]->immediate_work_queue(), + selector_.SelectWorkQueueToService( + TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); + + task_queues_[0]->SetQueueEnabled(false); + selector_.DisableQueue(task_queues_[0].get()); + + EXPECT_EQ(task_queues_[1]->delayed_work_queue(), + selector_.SelectWorkQueueToService()); + EXPECT_EQ(task_queues_[2]->immediate_work_queue(), + selector_.SelectWorkQueueToService( + TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); + + task_queues_[1]->SetQueueEnabled(false); + selector_.DisableQueue(task_queues_[1].get()); + + EXPECT_EQ(task_queues_[2]->immediate_work_queue(), + selector_.SelectWorkQueueToService( + TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); + EXPECT_EQ(task_queues_[2]->immediate_work_queue(), + selector_.SelectWorkQueueToService()); +} + TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) { TaskQueueSelectorForTest selector(associated_thread_); MockObserver mock_observer; diff --git a/chromium/base/task/sequence_manager/thread_controller_impl.cc b/chromium/base/task/sequence_manager/thread_controller_impl.cc index ab55a0cc91e..15b9ae60306 100644 --- a/chromium/base/task/sequence_manager/thread_controller_impl.cc +++ b/chromium/base/task/sequence_manager/thread_controller_impl.cc @@ -13,7 +13,7 @@ #include "base/task/sequence_manager/lazy_now.h" #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/sequenced_task_source.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" namespace base { namespace sequence_manager { diff --git a/chromium/base/task/sequence_manager/thread_controller_power_monitor.cc b/chromium/base/task/sequence_manager/thread_controller_power_monitor.cc new file mode 100644 index 00000000000..12dcb126110 --- /dev/null +++ b/chromium/base/task/sequence_manager/thread_controller_power_monitor.cc @@ -0,0 +1,91 @@ +// Copyright 2020 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 "base/task/sequence_manager/thread_controller_power_monitor.h" + +#include "base/feature_list.h" +#include "base/power_monitor/power_monitor.h" +#include "base/trace_event/base_tracing.h" + +namespace base { +namespace sequence_manager { +namespace internal { + +namespace { + +// Activate the power management events that affect task scheduling. +const Feature kUsePowerMonitorWithThreadController{ + "UsePowerMonitorWithThreadController", FEATURE_DISABLED_BY_DEFAULT}; + +// TODO(1074332): Remove this when the experiment becomes the default. +bool g_use_thread_controller_power_monitor_ = false; + +} // namespace + +ThreadControllerPowerMonitor::ThreadControllerPowerMonitor() = default; + +ThreadControllerPowerMonitor::~ThreadControllerPowerMonitor() { + PowerMonitor::RemoveObserver(this); +} + +void ThreadControllerPowerMonitor::BindToCurrentThread() { + // Occasionally registration happens twice (i.e. when the deprecated + // ThreadController::SetDefaultTaskRunner() re-initializes the + // ThreadController). + if (is_observer_registered_) + PowerMonitor::RemoveObserver(this); + + // Register the observer to deliver notifications on the current thread. + PowerMonitor::AddObserver(this); + is_observer_registered_ = true; +} + +bool ThreadControllerPowerMonitor::IsProcessInPowerSuspendState() { + return is_power_suspended_; +} + +// static +void ThreadControllerPowerMonitor::InitializeOnMainThread() { + DCHECK(!g_use_thread_controller_power_monitor_); + g_use_thread_controller_power_monitor_ = + FeatureList::IsEnabled(kUsePowerMonitorWithThreadController); +} + +// static +void ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting( + bool use_power_monitor) { + g_use_thread_controller_power_monitor_ = use_power_monitor; +} + +// static +void ThreadControllerPowerMonitor::ResetForTesting() { + g_use_thread_controller_power_monitor_ = false; +} + +void ThreadControllerPowerMonitor::OnSuspend() { + if (!g_use_thread_controller_power_monitor_) + return; + DCHECK(!is_power_suspended_); + + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("base", "ThreadController::Suspended", + this); + is_power_suspended_ = true; +} + +void ThreadControllerPowerMonitor::OnResume() { + if (!g_use_thread_controller_power_monitor_) + return; + + // It is possible a suspend was already happening before the observer was + // added to the power monitor. Ignoring the resume notification in that case. + if (is_power_suspended_) { + TRACE_EVENT_NESTABLE_ASYNC_END0("base", "ThreadController::Suspended", + this); + is_power_suspended_ = false; + } +} + +} // namespace internal +} // namespace sequence_manager +} // namespace base diff --git a/chromium/base/task/sequence_manager/thread_controller_power_monitor.h b/chromium/base/task/sequence_manager/thread_controller_power_monitor.h new file mode 100644 index 00000000000..46b44c8d85f --- /dev/null +++ b/chromium/base/task/sequence_manager/thread_controller_power_monitor.h @@ -0,0 +1,56 @@ +// Copyright 2020 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 BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_POWER_MONITOR_H_ +#define BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_POWER_MONITOR_H_ + +#include "base/power_monitor/power_observer.h" + +namespace base { +namespace sequence_manager { +namespace internal { + +// A helper class that keeps track of the power state and handles power +// notifications. The class register itself to the PowerMonitor and receives +// notifications on the bound thread (see BindToCurrentThread(...)). +class BASE_EXPORT ThreadControllerPowerMonitor : public PowerObserver { + public: + ThreadControllerPowerMonitor(); + ~ThreadControllerPowerMonitor() override; + ThreadControllerPowerMonitor(const ThreadControllerPowerMonitor&) = delete; + ThreadControllerPowerMonitor& operator=(const ThreadControllerPowerMonitor&) = + delete; + + // Register this class to the power monitor to receive notifications on this + // thread. It is safe to call this before PowerMonitor is initialized. + void BindToCurrentThread(); + + // Returns whether the process is between power suspend and resume + // notifications. + bool IsProcessInPowerSuspendState(); + + // Initialize the ThreadControllerPowerMonitor. Must be called once on the + // main thread during startup while single-threaded. + static void InitializeOnMainThread(); + + static void OverrideUsePowerMonitorForTesting(bool use_power_monitor); + static void ResetForTesting(); + + // base::PowerObserver: + void OnSuspend() override; + void OnResume() override; + + private: + // Power state based on notifications delivered to this observer. + bool is_power_suspended_ = false; + + // Whether PowerMonitor observer is registered. + bool is_observer_registered_ = false; +}; + +} // namespace internal +} // namespace sequence_manager +} // namespace base + +#endif // BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_POWER_MONITOR_H_ diff --git a/chromium/base/task/sequence_manager/thread_controller_power_monitor_unittest.cc b/chromium/base/task/sequence_manager/thread_controller_power_monitor_unittest.cc new file mode 100644 index 00000000000..72f91ad39d5 --- /dev/null +++ b/chromium/base/task/sequence_manager/thread_controller_power_monitor_unittest.cc @@ -0,0 +1,69 @@ +// Copyright 2020 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 "base/task/sequence_manager/thread_controller_power_monitor.h" + +#include "base/power_monitor/power_monitor.h" +#include "base/power_monitor/power_monitor_source.h" +#include "base/test/power_monitor_test_base.h" +#include "base/test/task_environment.h" + +#include "base/test/mock_callback.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace sequence_manager { +namespace internal { + +class ThreadControllerPowerMonitorTest : public testing::Test { + public: + void SetUp() override { + power_monitor_source_ = new PowerMonitorTestSource(); + PowerMonitor::Initialize( + std::unique_ptr<PowerMonitorSource>(power_monitor_source_)); + thread_controller_power_monitor_ = + std::make_unique<ThreadControllerPowerMonitor>(); + internal::ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting( + true); + } + + void TearDown() override { + thread_controller_power_monitor_.reset(); + internal::ThreadControllerPowerMonitor::ResetForTesting(); + PowerMonitor::ShutdownForTesting(); + } + + protected: + base::test::SingleThreadTaskEnvironment task_environment_; + PowerMonitorTestSource* power_monitor_source_ = nullptr; + std::unique_ptr<ThreadControllerPowerMonitor> + thread_controller_power_monitor_; +}; + +TEST_F(ThreadControllerPowerMonitorTest, IsProcessInPowerSuspendState) { + EXPECT_FALSE( + thread_controller_power_monitor_->IsProcessInPowerSuspendState()); + + // Before the monitor is bound to the thread, the notifications are not + // received. + power_monitor_source_->GenerateSuspendEvent(); + EXPECT_FALSE( + thread_controller_power_monitor_->IsProcessInPowerSuspendState()); + power_monitor_source_->GenerateResumeEvent(); + EXPECT_FALSE( + thread_controller_power_monitor_->IsProcessInPowerSuspendState()); + + thread_controller_power_monitor_->BindToCurrentThread(); + + // Ensures notifications are processed. + power_monitor_source_->GenerateSuspendEvent(); + EXPECT_TRUE(thread_controller_power_monitor_->IsProcessInPowerSuspendState()); + power_monitor_source_->GenerateResumeEvent(); + EXPECT_FALSE( + thread_controller_power_monitor_->IsProcessInPowerSuspendState()); +} + +} // namespace internal +} // namespace sequence_manager +} // namespace base diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc index f225da8b584..590e8297807 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc @@ -4,14 +4,16 @@ #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" +#include <algorithm> +#include <utility> + #include "base/auto_reset.h" -#include "base/feature_list.h" +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_pump.h" -#include "base/power_monitor/power_monitor.h" #include "base/threading/hang_watcher.h" #include "base/time/tick_clock.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" #include "build/build_config.h" #if defined(OS_IOS) @@ -25,12 +27,6 @@ namespace sequence_manager { namespace internal { namespace { -// Activate the power management events that affect the tasks scheduling. -const Feature kUsePowerMonitorWithThreadController{ - "UsePowerMonitorWithThreadController", FEATURE_DISABLED_BY_DEFAULT}; - -bool g_use_power_monitor_with_thread_controller = false; - // Returns |next_run_time| capped at 1 day from |lazy_now|. This is used to // mitigate https://crbug.com/850450 where some platforms are unhappy with // delays > 100,000,000 seconds. In practice, a diagnosis metric showed that no @@ -183,6 +179,9 @@ void ThreadControllerWithMessagePumpImpl::InitializeThreadTaskRunnerHandle() { main_thread_only().thread_task_runner_handle.reset(); main_thread_only().thread_task_runner_handle = std::make_unique<ThreadTaskRunnerHandle>(task_runner_); + // When the task runner is known, bind the power manager. Power notifications + // are received through that sequence. + power_monitor_.BindToCurrentThread(); } scoped_refptr<SingleThreadTaskRunner> @@ -306,7 +305,12 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl( DCHECK(main_thread_only().task_source); for (int i = 0; i < main_thread_only().work_batch_size; i++) { - Task* task = main_thread_only().task_source->SelectNextTask(); + const SequencedTaskSource::SelectTaskOption select_task_option = + power_monitor_.IsProcessInPowerSuspendState() + ? SequencedTaskSource::SelectTaskOption::kSkipDelayedTask + : SequencedTaskSource::SelectTaskOption::kDefault; + Task* task = + main_thread_only().task_source->SelectNextTask(select_task_option); if (!task) break; @@ -351,8 +355,14 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl( work_deduplicator_.WillCheckForMoreWork(); - TimeDelta do_work_delay = - main_thread_only().task_source->DelayTillNextTask(continuation_lazy_now); + // Re-check the state of the power after running tasks. An executed task may + // have been a power change notification. + const SequencedTaskSource::SelectTaskOption select_task_option = + power_monitor_.IsProcessInPowerSuspendState() + ? SequencedTaskSource::SelectTaskOption::kSkipDelayedTask + : SequencedTaskSource::SelectTaskOption::kDefault; + TimeDelta do_work_delay = main_thread_only().task_source->DelayTillNextTask( + continuation_lazy_now, select_task_option); DCHECK_GE(do_work_delay, TimeDelta()); return do_work_delay; } @@ -368,8 +378,7 @@ bool ThreadControllerWithMessagePumpImpl::DoIdleWork() { work_id_provider_->IncrementWorkId(); #if defined(OS_WIN) - if (!g_use_power_monitor_with_thread_controller || - !base::PowerMonitor::IsProcessSuspended()) { + if (!power_monitor_.IsProcessInPowerSuspendState()) { // Avoid calling Time::ActivateHighResolutionTimer() between // suspend/resume as the system hangs if we do (crbug.com/1074028). // OnResume() will generate a task on this thread per the @@ -532,11 +541,5 @@ bool ThreadControllerWithMessagePumpImpl::ShouldQuitRunLoopWhenIdle() { } } // namespace internal - -void PostFieldTrialInitialization() { - internal::g_use_power_monitor_with_thread_controller = - FeatureList::IsEnabled(internal::kUsePowerMonitorWithThreadController); -} - } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h index 0dbf946f9ea..7a153d44485 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h @@ -17,6 +17,7 @@ #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/sequenced_task_source.h" #include "base/task/sequence_manager/thread_controller.h" +#include "base/task/sequence_manager/thread_controller_power_monitor.h" #include "base/task/sequence_manager/work_deduplicator.h" #include "base/thread_annotations.h" #include "base/threading/hang_watcher.h" @@ -94,17 +95,6 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl void Quit() override; void EnsureWorkScheduled() override; - private: - friend class DoWorkScope; - friend class RunScope; - - // Returns the delay till the next task. If there's no delay TimeDelta::Max() - // will be returned. - TimeDelta DoWorkImpl(LazyNow* continuation_lazy_now); - - void InitializeThreadTaskRunnerHandle() - EXCLUSIVE_LOCKS_REQUIRED(task_runner_lock_); - struct MainThreadOnly { MainThreadOnly(); ~MainThreadOnly(); @@ -134,6 +124,25 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl bool task_execution_allowed = true; }; + const MainThreadOnly& MainThreadOnlyForTesting() const { + return main_thread_only_; + } + + ThreadControllerPowerMonitor* ThreadControllerPowerMonitorForTesting() { + return &power_monitor_; + } + + private: + friend class DoWorkScope; + friend class RunScope; + + // Returns the delay till the next task. If there's no delay TimeDelta::Max() + // will be returned. + TimeDelta DoWorkImpl(LazyNow* continuation_lazy_now); + + void InitializeThreadTaskRunnerHandle() + EXCLUSIVE_LOCKS_REQUIRED(task_runner_lock_); + MainThreadOnly& main_thread_only() { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); return main_thread_only_; @@ -154,6 +163,8 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl WorkDeduplicator work_deduplicator_; + ThreadControllerPowerMonitor power_monitor_; + // Can only be set once (just before calling // work_deduplicator_.BindToCurrentThread()). After that only read access is // allowed. @@ -187,11 +198,6 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl }; } // namespace internal - -// Initialize ThreadController features. Called after FeatureList is available -// when the process is still single-threaded. -BASE_EXPORT void PostFieldTrialInitialization(); - } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc index b5e252fd800..8fcda55a02a 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc @@ -4,19 +4,24 @@ #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" +#include <queue> +#include <string> +#include <utility> +#include <vector> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/scoped_refptr.h" #include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/thread_controller_power_monitor.h" #include "base/test/bind_test_util.h" #include "base/test/mock_callback.h" #include "base/test/simple_test_tick_clock.h" #include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include <queue> - using testing::_; using testing::Invoke; using testing::ElementsAre; @@ -30,7 +35,7 @@ class ThreadControllerForTest : public internal::ThreadControllerWithMessagePumpImpl { public: ThreadControllerForTest(std::unique_ptr<MessagePump> pump, - SequenceManager::Settings& settings) + const SequenceManager::Settings& settings) : ThreadControllerWithMessagePumpImpl(std::move(pump), settings) {} using ThreadControllerWithMessagePumpImpl::DoIdleWork; @@ -38,6 +43,10 @@ class ThreadControllerForTest using ThreadControllerWithMessagePumpImpl::EnsureWorkScheduled; using ThreadControllerWithMessagePumpImpl::Quit; using ThreadControllerWithMessagePumpImpl::Run; + + using ThreadControllerWithMessagePumpImpl::MainThreadOnlyForTesting; + using ThreadControllerWithMessagePumpImpl:: + ThreadControllerPowerMonitorForTesting; }; class MockMessagePump : public MessagePump { @@ -78,11 +87,15 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { explicit FakeSequencedTaskSource(TickClock* clock) : clock_(clock) {} ~FakeSequencedTaskSource() override = default; - Task* SelectNextTask() override { + Task* SelectNextTask(SelectTaskOption option) override { if (tasks_.empty()) return nullptr; if (tasks_.front().delayed_run_time > clock_->NowTicks()) return nullptr; + if (option == SequencedTaskSource::SelectTaskOption::kSkipDelayedTask && + !tasks_.front().delayed_run_time.is_null()) { + return nullptr; + } running_stack_.push_back(std::move(tasks_.front())); tasks_.pop(); return &running_stack_.back(); @@ -90,9 +103,14 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { void DidRunTask() override { running_stack_.pop_back(); } - TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override { + TimeDelta DelayTillNextTask(LazyNow* lazy_now, + SelectTaskOption option) const override { if (tasks_.empty()) return TimeDelta::Max(); + if (option == SequencedTaskSource::SelectTaskOption::kSkipDelayedTask && + !tasks_.front().delayed_run_time.is_null()) { + return TimeDelta::Max(); + } if (tasks_.front().delayed_run_time.is_null()) return TimeDelta(); if (lazy_now->Now() > tasks_.front().delayed_run_time) @@ -110,7 +128,13 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { delayed_run_time, EnqueueOrder::FromIntForTesting(13))); } - bool HasPendingHighResolutionTasks() override { return false; } + bool HasPendingHighResolutionTasks() override { + return has_pending_high_resolution_tasks; + } + + void SetHasPendingHighResolutionTasks(bool state) { + has_pending_high_resolution_tasks = state; + } bool OnSystemIdle() override { return false; } @@ -118,6 +142,7 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource { TickClock* clock_; std::queue<Task> tasks_; std::vector<Task> running_stack_; + bool has_pending_high_resolution_tasks = false; }; TimeTicks Seconds(int seconds) { @@ -143,6 +168,15 @@ class ThreadControllerWithMessagePumpTest : public testing::Test { thread_controller_.SetSequencedTaskSource(&task_source_); } + void SetUp() override { + internal::ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting( + true); + } + + void TearDown() override { + internal::ThreadControllerPowerMonitor::ResetForTesting(); + } + protected: MockMessagePump* message_pump_; SequenceManager::Settings settings_; @@ -578,5 +612,131 @@ TEST_F(ThreadControllerWithMessagePumpTest, RunWithTimeout) { thread_controller_.Run(true, TimeDelta::FromSeconds(15)); } +#if defined(OS_WIN) +TEST_F(ThreadControllerWithMessagePumpTest, SetHighResolutionTimer) { + MockCallback<OnceClosure> task; + task_source_.AddTask(FROM_HERE, task.Get(), Seconds(5)); + + ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>()); + + EXPECT_CALL(*message_pump_, Run(_)) + .WillOnce(Invoke([&](MessagePump::Delegate* delegate) { + // Should initially not be in high resolution. + EXPECT_FALSE( + thread_controller_.MainThreadOnlyForTesting().in_high_res_mode); + + // Ensures timer resolution is set to high resolution. + task_source_.SetHasPendingHighResolutionTasks(true); + EXPECT_FALSE(delegate->DoIdleWork()); + EXPECT_TRUE( + thread_controller_.MainThreadOnlyForTesting().in_high_res_mode); + + // Ensures time resolution is set back to low resolution. + task_source_.SetHasPendingHighResolutionTasks(false); + EXPECT_FALSE(delegate->DoIdleWork()); + EXPECT_FALSE( + thread_controller_.MainThreadOnlyForTesting().in_high_res_mode); + + EXPECT_CALL(*message_pump_, Quit()); + thread_controller_.Quit(); + })); + + RunLoop run_loop; + run_loop.Run(); +} +#endif // OS_WIN + +#if defined(OS_WIN) +TEST_F(ThreadControllerWithMessagePumpTest, + SetHighResolutionTimerWithPowerSuspend) { + MockCallback<OnceClosure> task; + task_source_.AddTask(FROM_HERE, task.Get(), Seconds(5)); + + ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>()); + + EXPECT_CALL(*message_pump_, Run(_)) + .WillOnce(Invoke([&](MessagePump::Delegate* delegate) { + // Should initially not be in high resolution. + EXPECT_FALSE( + thread_controller_.MainThreadOnlyForTesting().in_high_res_mode); + + // The power suspend notification is sent. + thread_controller_.ThreadControllerPowerMonitorForTesting() + ->OnSuspend(); + + // The timer resolution should NOT be updated during power suspend. + task_source_.SetHasPendingHighResolutionTasks(true); + EXPECT_FALSE(delegate->DoIdleWork()); + EXPECT_FALSE( + thread_controller_.MainThreadOnlyForTesting().in_high_res_mode); + + // The power resume notification is sent. + thread_controller_.ThreadControllerPowerMonitorForTesting()->OnResume(); + + // Ensures timer resolution is set to high resolution. + EXPECT_FALSE(delegate->DoIdleWork()); + EXPECT_TRUE( + thread_controller_.MainThreadOnlyForTesting().in_high_res_mode); + + EXPECT_CALL(*message_pump_, Quit()); + thread_controller_.Quit(); + })); + + RunLoop run_loop; + run_loop.Run(); +} +#endif // OS_WIN + +TEST_F(ThreadControllerWithMessagePumpTest, + ScheduleDelayedWorkWithPowerSuspend) { + ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>()); + + MockCallback<OnceClosure> task1; + task_source_.AddTask(FROM_HERE, task1.Get(), Seconds(10)); + MockCallback<OnceClosure> task2; + task_source_.AddTask(FROM_HERE, task2.Get(), Seconds(15)); + + clock_.SetNowTicks(Seconds(5)); + + // Call a no-op DoWork. Expect that it doesn't do any work. + EXPECT_CALL(task1, Run()).Times(0); + EXPECT_CALL(task2, Run()).Times(0); + EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, Seconds(10)); + testing::Mock::VerifyAndClearExpectations(&task1); + testing::Mock::VerifyAndClearExpectations(&task2); + + // Simulate a power suspend. + thread_controller_.ThreadControllerPowerMonitorForTesting()->OnSuspend(); + + // Delayed task is not yet ready to be executed. + EXPECT_CALL(task1, Run()).Times(0); + EXPECT_CALL(task2, Run()).Times(0); + EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, TimeTicks::Max()); + testing::Mock::VerifyAndClearExpectations(&task1); + testing::Mock::VerifyAndClearExpectations(&task2); + + // Move time after the expiration delay of tasks. + clock_.SetNowTicks(Seconds(17)); + + // Should not process delayed tasks. The process is still in suspended power + // state. + EXPECT_CALL(task1, Run()).Times(0); + EXPECT_CALL(task2, Run()).Times(0); + EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, TimeTicks::Max()); + testing::Mock::VerifyAndClearExpectations(&task1); + testing::Mock::VerifyAndClearExpectations(&task2); + + // Simulate a power resume. + thread_controller_.ThreadControllerPowerMonitorForTesting()->OnResume(); + + // No longer in suspended state. Controller should process both delayed tasks. + EXPECT_CALL(task1, Run()).Times(1); + EXPECT_CALL(task2, Run()).Times(1); + EXPECT_TRUE(thread_controller_.DoWork().is_immediate()); + EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, TimeTicks::Max()); + testing::Mock::VerifyAndClearExpectations(&task1); + testing::Mock::VerifyAndClearExpectations(&task2); +} + } // namespace sequence_manager } // namespace base diff --git a/chromium/base/task/sequence_manager/time_domain.cc b/chromium/base/task/sequence_manager/time_domain.cc index 1df52f3d131..2a31f8b8143 100644 --- a/chromium/base/task/sequence_manager/time_domain.cc +++ b/chromium/base/task/sequence_manager/time_domain.cc @@ -140,20 +140,15 @@ Optional<TimeTicks> TimeDomain::NextScheduledRunTime() const { return delayed_wake_up_queue_.Min().wake_up.time; } -void TimeDomain::AsValueInto(trace_event::TracedValue* state) const { - state->BeginDictionary(); - state->SetString("name", GetName()); - state->SetInteger("registered_delay_count", delayed_wake_up_queue_.size()); +Value TimeDomain::AsValue() const { + Value state(Value::Type::DICTIONARY); + state.SetStringKey("name", GetName()); + state.SetIntKey("registered_delay_count", delayed_wake_up_queue_.size()); if (!delayed_wake_up_queue_.empty()) { TimeDelta delay = delayed_wake_up_queue_.Min().wake_up.time - Now(); - state->SetDouble("next_delay_ms", delay.InMillisecondsF()); + state.SetDoubleKey("next_delay_ms", delay.InMillisecondsF()); } - AsValueIntoInternal(state); - state->EndDictionary(); -} - -void TimeDomain::AsValueIntoInternal(trace_event::TracedValue* state) const { - // Can be overriden to trace some additional state. + return state; } } // namespace sequence_manager diff --git a/chromium/base/task/sequence_manager/time_domain.h b/chromium/base/task/sequence_manager/time_domain.h index ddbbc54bd96..6c3319bf0ee 100644 --- a/chromium/base/task/sequence_manager/time_domain.h +++ b/chromium/base/task/sequence_manager/time_domain.h @@ -8,12 +8,13 @@ #include <map> #include "base/callback.h" -#include "base/logging.h" +#include "base/check.h" #include "base/macros.h" #include "base/task/common/intrusive_heap.h" #include "base/task/sequence_manager/lazy_now.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/time/time.h" +#include "base/values.h" namespace base { namespace sequence_manager { @@ -56,7 +57,7 @@ class BASE_EXPORT TimeDomain { // NOTE: |lazy_now| and the return value are in the SequenceManager's time. virtual Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) = 0; - void AsValueInto(trace_event::TracedValue* state) const; + Value AsValue() const; bool has_pending_high_resolution_tasks() const { return pending_high_res_wake_up_count_; @@ -91,9 +92,6 @@ class BASE_EXPORT TimeDomain { // May be overriden to control wake ups manually. virtual void RequestDoWork(); - // For implementation-specific tracing. - virtual void AsValueIntoInternal(trace_event::TracedValue* state) const; - virtual const char* GetName() const = 0; // Called when the TimeDomain is registered. |sequence_manager| is expected to diff --git a/chromium/base/task/sequence_manager/time_domain_unittest.cc b/chromium/base/task/sequence_manager/time_domain_unittest.cc index 2096520fc16..8a5c16b4464 100644 --- a/chromium/base/task/sequence_manager/time_domain_unittest.cc +++ b/chromium/base/task/sequence_manager/time_domain_unittest.cc @@ -57,7 +57,6 @@ class TestTimeDomain : public TimeDomain { return false; } - void AsValueIntoInternal(trace_event::TracedValue* state) const override {} const char* GetName() const override { return "Test"; } internal::TaskQueueImpl* NextScheduledTaskQueue() const { diff --git a/chromium/base/task/sequence_manager/work_queue.cc b/chromium/base/task/sequence_manager/work_queue.cc index 836f00034b9..b3667285ad1 100644 --- a/chromium/base/task/sequence_manager/work_queue.cc +++ b/chromium/base/task/sequence_manager/work_queue.cc @@ -18,11 +18,11 @@ WorkQueue::WorkQueue(TaskQueueImpl* task_queue, QueueType queue_type) : task_queue_(task_queue), name_(name), queue_type_(queue_type) {} -void WorkQueue::AsValueInto(TimeTicks now, - trace_event::TracedValue* state) const { - for (const Task& task : tasks_) { - TaskQueueImpl::TaskAsValueInto(task, now, state); - } +Value WorkQueue::AsValue(TimeTicks now) const { + Value state(Value::Type::LIST); + for (const Task& task : tasks_) + state.Append(TaskQueueImpl::TaskAsValue(task, now)); + return state; } WorkQueue::~WorkQueue() { diff --git a/chromium/base/task/sequence_manager/work_queue.h b/chromium/base/task/sequence_manager/work_queue.h index 65fdee4ca28..77bdc127520 100644 --- a/chromium/base/task/sequence_manager/work_queue.h +++ b/chromium/base/task/sequence_manager/work_queue.h @@ -10,8 +10,7 @@ #include "base/task/sequence_manager/enqueue_order.h" #include "base/task/sequence_manager/sequenced_task_source.h" #include "base/task/sequence_manager/task_queue_impl.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/traced_value.h" +#include "base/values.h" namespace base { namespace sequence_manager { @@ -43,7 +42,7 @@ class BASE_EXPORT WorkQueue { // Assigns the current set index. void AssignSetIndex(size_t work_queue_set_index); - void AsValueInto(TimeTicks now, trace_event::TracedValue* state) const; + Value AsValue(TimeTicks now) const; // Returns true if the |tasks_| is empty. This method ignores any fences. bool Empty() const { return tasks_.empty(); } diff --git a/chromium/base/task/sequence_manager/work_queue_sets.h b/chromium/base/task/sequence_manager/work_queue_sets.h index f128c62c369..626debe0075 100644 --- a/chromium/base/task/sequence_manager/work_queue_sets.h +++ b/chromium/base/task/sequence_manager/work_queue_sets.h @@ -9,13 +9,13 @@ #include <map> #include "base/base_export.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/macros.h" #include "base/task/common/intrusive_heap.h" #include "base/task/sequence_manager/sequence_manager.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/work_queue.h" -#include "base/trace_event/traced_value.h" +#include "base/trace_event/base_tracing.h" namespace base { namespace sequence_manager { |