summaryrefslogtreecommitdiff
path: root/chromium/base/task/sequence_manager
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/base/task/sequence_manager
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/base/task/sequence_manager/lazily_deallocated_deque.h2
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.cc118
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.h22
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc187
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_perftest.cc1
-rw-r--r--chromium/base/task/sequence_manager/sequenced_task_source.h13
-rw-r--r--chromium/base/task/sequence_manager/task_queue_impl.cc140
-rw-r--r--chromium/base/task/sequence_manager/task_queue_impl.h23
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.cc55
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.h24
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector_unittest.cc69
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_impl.cc2
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_power_monitor.cc91
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_power_monitor.h56
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_power_monitor_unittest.cc69
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc43
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h38
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc172
-rw-r--r--chromium/base/task/sequence_manager/time_domain.cc17
-rw-r--r--chromium/base/task/sequence_manager/time_domain.h8
-rw-r--r--chromium/base/task/sequence_manager/time_domain_unittest.cc1
-rw-r--r--chromium/base/task/sequence_manager/work_queue.cc10
-rw-r--r--chromium/base/task/sequence_manager/work_queue.h5
-rw-r--r--chromium/base/task/sequence_manager/work_queue_sets.h4
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 {