diff options
Diffstat (limited to 'chromium/base/task')
45 files changed, 3223 insertions, 317 deletions
diff --git a/chromium/base/task/common/checked_lock.h b/chromium/base/task/common/checked_lock.h index 29ce5735b61..4399ec477a5 100644 --- a/chromium/base/task/common/checked_lock.h +++ b/chromium/base/task/common/checked_lock.h @@ -31,18 +31,27 @@ namespace internal { // CheckedLock(const CheckedLock* predecessor) // Constructor that specifies an allowed predecessor for that lock. // DCHECKs -// On Construction if |predecessor| forms a predecessor lock cycle. +// On Construction if |predecessor| forms a predecessor lock cycle or +// is a universal successor. // On Acquisition if the previous lock acquired on the thread is not // either |predecessor| or a universal predecessor. Okay if there // was no previous lock acquired. // // CheckedLock(UniversalPredecessor universal_predecessor) // Constructor for a lock that will allow the acquisition of any lock after -// it, without needing to explicitly be named a predecessor. Can only be -// acquired if no locks are currently held by this thread. -// DCHECKs +// it, without needing to explicitly be named a predecessor (e.g. a root in +// a lock chain). Can only be acquired if no locks are currently held by +// this thread. DCHECKs // On Acquisition if any CheckedLock is acquired on this thread. // +// CheckedLock(UniversalSuccessor universal_successor) +// Constructor for a lock that will allow its acquisition after any other +// lock, without needing to explicitly name its predecessor (e.g. a leaf in +// a lock chain). Can not be acquired after another UniversalSuccessor lock. +// DCHECKs +// On Acquisition if there was a previously acquired lock on the thread +// and it was also a universal successor. +// // void Acquire() // Acquires the lock. // @@ -63,6 +72,8 @@ class LOCKABLE CheckedLock : public CheckedLockImpl { : CheckedLockImpl(predecessor) {} explicit CheckedLock(UniversalPredecessor universal_predecessor) : CheckedLockImpl(universal_predecessor) {} + explicit CheckedLock(UniversalSuccessor universal_successor) + : CheckedLockImpl(universal_successor) {} }; #else // DCHECK_IS_ON() class LOCKABLE CheckedLock : public Lock { @@ -70,6 +81,7 @@ class LOCKABLE CheckedLock : public Lock { CheckedLock() = default; explicit CheckedLock(const CheckedLock*) {} explicit CheckedLock(UniversalPredecessor) {} + explicit CheckedLock(UniversalSuccessor) {} static void AssertNoLockHeldOnCurrentThread() {} std::unique_ptr<ConditionVariable> CreateConditionVariable() { diff --git a/chromium/base/task/common/checked_lock_impl.cc b/chromium/base/task/common/checked_lock_impl.cc index 698886e1615..8b41e95cf8c 100644 --- a/chromium/base/task/common/checked_lock_impl.cc +++ b/chromium/base/task/common/checked_lock_impl.cc @@ -81,7 +81,12 @@ class SafeAcquisitionTracker { // Using at() is exception-safe here as |lock| was registered already. const CheckedLockImpl* allowed_predecessor = allowed_predecessor_map_.at(lock); - DCHECK_EQ(previous_lock, allowed_predecessor); + if (lock->is_universal_successor()) { + DCHECK(!previous_lock->is_universal_successor()); + return; + } else { + DCHECK_EQ(previous_lock, allowed_predecessor); + } } // Asserts that |lock|'s registered predecessor is safe. Because @@ -134,12 +139,18 @@ CheckedLockImpl::CheckedLockImpl() : CheckedLockImpl(nullptr) {} CheckedLockImpl::CheckedLockImpl(const CheckedLockImpl* predecessor) : is_universal_predecessor_(false) { + DCHECK(predecessor == nullptr || !predecessor->is_universal_successor_); g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor); } CheckedLockImpl::CheckedLockImpl(UniversalPredecessor) : is_universal_predecessor_(true) {} +CheckedLockImpl::CheckedLockImpl(UniversalSuccessor) + : is_universal_successor_(true) { + g_safe_acquisition_tracker.Get().RegisterLock(this, nullptr); +} + CheckedLockImpl::~CheckedLockImpl() { g_safe_acquisition_tracker.Get().UnregisterLock(this); } diff --git a/chromium/base/task/common/checked_lock_impl.h b/chromium/base/task/common/checked_lock_impl.h index acb1d133753..88aba042aad 100644 --- a/chromium/base/task/common/checked_lock_impl.h +++ b/chromium/base/task/common/checked_lock_impl.h @@ -18,6 +18,7 @@ class ConditionVariable; namespace internal { struct UniversalPredecessor {}; +struct UniversalSuccessor {}; // A regular lock with simple deadlock correctness checking. // This lock tracks all of the available locks to make sure that any locks are @@ -28,6 +29,7 @@ class BASE_EXPORT CheckedLockImpl { CheckedLockImpl(); explicit CheckedLockImpl(const CheckedLockImpl* predecessor); explicit CheckedLockImpl(UniversalPredecessor); + explicit CheckedLockImpl(UniversalSuccessor); ~CheckedLockImpl(); static void AssertNoLockHeldOnCurrentThread(); @@ -40,10 +42,12 @@ class BASE_EXPORT CheckedLockImpl { std::unique_ptr<ConditionVariable> CreateConditionVariable(); bool is_universal_predecessor() const { return is_universal_predecessor_; } + bool is_universal_successor() const { return is_universal_successor_; } private: Lock lock_; - const bool is_universal_predecessor_; + const bool is_universal_predecessor_ = false; + const bool is_universal_successor_ = false; DISALLOW_COPY_AND_ASSIGN(CheckedLockImpl); }; diff --git a/chromium/base/task/common/checked_lock_unittest.cc b/chromium/base/task/common/checked_lock_unittest.cc index 54b74c50391..2e21eace50b 100644 --- a/chromium/base/task/common/checked_lock_unittest.cc +++ b/chromium/base/task/common/checked_lock_unittest.cc @@ -307,7 +307,7 @@ TEST(CheckedLockTest, AcquireMultipleLocksAfterUniversalPredecessor) NO_THREAD_SAFETY_ANALYSIS { // Acquisition of a universal-predecessor lock does not affect acquisition // rules for locks beyond the one acquired directly after it. - CheckedLock universal_predecessor((UniversalPredecessor())); + CheckedLock universal_predecessor{UniversalPredecessor()}; CheckedLock lock; CheckedLock lock2(&lock); CheckedLock lock3; @@ -329,7 +329,7 @@ NO_THREAD_SAFETY_ANALYSIS { TEST(CheckedLockTest, AcquireUniversalPredecessorAfterLock) NO_THREAD_SAFETY_ANALYSIS { // A universal-predecessor lock may not be acquired after any other lock. - CheckedLock universal_predecessor((UniversalPredecessor())); + CheckedLock universal_predecessor{UniversalPredecessor()}; CheckedLock lock; EXPECT_DCHECK_DEATH({ @@ -342,8 +342,8 @@ TEST(CheckedLockTest, AcquireUniversalPredecessorAfterUniversalPredecessor) NO_THREAD_SAFETY_ANALYSIS { // A universal-predecessor lock may not be acquired after any other lock, not // even another universal predecessor. - CheckedLock universal_predecessor((UniversalPredecessor())); - CheckedLock universal_predecessor2((UniversalPredecessor())); + CheckedLock universal_predecessor{UniversalPredecessor()}; + CheckedLock universal_predecessor2{UniversalPredecessor()}; EXPECT_DCHECK_DEATH({ universal_predecessor.Acquire(); @@ -351,6 +351,70 @@ NO_THREAD_SAFETY_ANALYSIS { }); } +TEST(CheckedLockTest, AcquireLockBeforeUniversalSuccessor) { + // Acquisition of a universal-successor lock should be allowed + // after any other acquisition. + CheckedLock universal_successor{UniversalSuccessor()}; + CheckedLock lock; + + lock.Acquire(); + universal_successor.Acquire(); + universal_successor.Release(); + lock.Release(); +} + +TEST(CheckedLockTest, AcquireMultipleLocksBeforeAndAfterUniversalSuccessor) +NO_THREAD_SAFETY_ANALYSIS { + // Acquisition of a universal-successor lock does not affect acquisition + // rules for locks beyond the one acquired directly after it. + CheckedLock lock; + CheckedLock universal_successor{UniversalSuccessor()}; + CheckedLock lock2; + + lock.Acquire(); + universal_successor.Acquire(); + universal_successor.Release(); + lock.Release(); + + EXPECT_DCHECK_DEATH({ + universal_successor.Acquire(); + lock2.Acquire(); + }); +} + +TEST(CheckedLockTest, AcquireUniversalSuccessorBeforeLock) +NO_THREAD_SAFETY_ANALYSIS { + // A universal-successor lock may not be acquired before any other lock. + CheckedLock universal_successor{UniversalSuccessor()}; + CheckedLock lock; + + EXPECT_DCHECK_DEATH({ + universal_successor.Acquire(); + lock.Acquire(); + }); +} + +TEST(CheckedLockTest, AcquireUniversalSuccessorAfterUniversalSuccessor) +NO_THREAD_SAFETY_ANALYSIS { + // A universal-successor lock may not be acquired before any other lock, not + // even another universal successor. + CheckedLock universal_successor{UniversalSuccessor()}; + CheckedLock universal_successor2{UniversalSuccessor()}; + + EXPECT_DCHECK_DEATH({ + universal_successor.Acquire(); + universal_successor2.Acquire(); + }); +} + +TEST(CheckedLockTest, UniversalSuccessorAsPredecessor) +NO_THREAD_SAFETY_ANALYSIS { + // A universal-successor lock cannot be declared as a predecessor to + // any other lock. + CheckedLock universal_successor{UniversalSuccessor()}; + EXPECT_DCHECK_DEATH({ CheckedLock banned_successor(&universal_successor); }); +} + TEST(CheckedLockTest, AssertNoLockHeldOnCurrentThread) { // AssertNoLockHeldOnCurrentThread() shouldn't fail when no lock is acquired. CheckedLock::AssertNoLockHeldOnCurrentThread(); diff --git a/chromium/base/task/common/task_annotator.cc b/chromium/base/task/common/task_annotator.cc index 1001359c712..505a55b0e49 100644 --- a/chromium/base/task/common/task_annotator.cc +++ b/chromium/base/task/common/task_annotator.cc @@ -10,7 +10,7 @@ #include "base/debug/alias.h" #include "base/no_destructor.h" #include "base/threading/thread_local.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" namespace base { @@ -64,10 +64,10 @@ void TaskAnnotator::WillQueueTask(const char* trace_event_name, DCHECK(trace_event_name); DCHECK(pending_task); DCHECK(task_queue_name); - TRACE_EVENT_WITH_FLOW1( - TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), trace_event_name, - TRACE_ID_LOCAL(GetTaskTraceID(*pending_task)), TRACE_EVENT_FLAG_FLOW_OUT, - "task_queue_name", task_queue_name); + TRACE_EVENT_WITH_FLOW1("toplevel.flow", trace_event_name, + TRACE_ID_LOCAL(GetTaskTraceID(*pending_task)), + TRACE_EVENT_FLAG_FLOW_OUT, "task_queue_name", + task_queue_name); DCHECK(!pending_task->task_backtrace[0]) << "Task backtrace was already set, task posted twice??"; @@ -98,9 +98,9 @@ void TaskAnnotator::RunTask(const char* trace_event_name, TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc"), "TaskAnnotator::RunTask", "ipc_hash", pending_task->ipc_hash); - TRACE_EVENT_WITH_FLOW0( - TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), trace_event_name, - TRACE_ID_LOCAL(GetTaskTraceID(*pending_task)), TRACE_EVENT_FLAG_FLOW_IN); + TRACE_EVENT_WITH_FLOW0("toplevel.flow", trace_event_name, + TRACE_ID_LOCAL(GetTaskTraceID(*pending_task)), + TRACE_EVENT_FLAG_FLOW_IN); // Before running the task, store the IPC context and the task backtrace with // the chain of PostTasks that resulted in this call and deliberately alias it diff --git a/chromium/base/task/post_job.h b/chromium/base/task/post_job.h index 1d396f1fb11..3ae31867e2b 100644 --- a/chromium/base/task/post_job.h +++ b/chromium/base/task/post_job.h @@ -7,8 +7,8 @@ #include "base/base_export.h" #include "base/callback.h" +#include "base/check_op.h" #include "base/location.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/task/task_traits.h" diff --git a/chromium/base/task/post_task.cc b/chromium/base/task/post_task.cc index 12218599e48..744d0043520 100644 --- a/chromium/base/task/post_task.cc +++ b/chromium/base/task/post_task.cc @@ -62,19 +62,6 @@ TaskExecutor* GetTaskExecutorForTraits(const TaskTraits& traits) { } // namespace -bool PostTask(const Location& from_here, OnceClosure task) { - // TODO(skyostil): Make task traits required here too. - return PostDelayedTask(from_here, {ThreadPool()}, std::move(task), - TimeDelta()); -} - -bool PostTaskAndReply(const Location& from_here, - OnceClosure task, - OnceClosure reply) { - return PostTaskAndReply(from_here, {ThreadPool()}, std::move(task), - std::move(reply)); -} - bool PostTask(const Location& from_here, const TaskTraits& traits, OnceClosure task) { diff --git a/chromium/base/task/post_task.h b/chromium/base/task/post_task.h index 8bcd24fad1c..84b959771b1 100644 --- a/chromium/base/task/post_task.h +++ b/chromium/base/task/post_task.h @@ -99,27 +99,6 @@ namespace base { // have to worry about this. You will encounter DCHECKs or nullptr dereferences // if this is violated. For tests, prefer base::test::TaskEnvironment. -// Equivalent to calling PostTask with default TaskTraits. -BASE_EXPORT bool PostTask(const Location& from_here, OnceClosure task); -inline bool PostTask(OnceClosure task, - const Location& from_here = Location::Current()) { - return PostTask(from_here, std::move(task)); -} - -// Equivalent to calling PostTaskAndReply with default TaskTraits. -BASE_EXPORT bool PostTaskAndReply(const Location& from_here, - OnceClosure task, - OnceClosure reply); - -// Equivalent to calling PostTaskAndReplyWithResult with default TaskTraits. -template <typename TaskReturnType, typename ReplyArgType> -bool PostTaskAndReplyWithResult(const Location& from_here, - OnceCallback<TaskReturnType()> task, - OnceCallback<void(ReplyArgType)> reply) { - return PostTaskAndReplyWithResult(from_here, {ThreadPool()}, std::move(task), - std::move(reply)); -} - // Posts |task| with specific |traits|. Returns false if the task definitely // won't run because of current shutdown state. BASE_EXPORT bool PostTask(const Location& from_here, diff --git a/chromium/base/task/post_task_unittest.cc b/chromium/base/task/post_task_unittest.cc index cbbbe666b58..b54872791a4 100644 --- a/chromium/base/task/post_task_unittest.cc +++ b/chromium/base/task/post_task_unittest.cc @@ -99,10 +99,6 @@ class PostTaskTestWithExecutor : public ::testing::Test { }; TEST_F(PostTaskTestWithExecutor, PostTaskToThreadPool) { - // Tasks without extension should not go to the TestTaskExecutor. - EXPECT_TRUE(PostTask(FROM_HERE, DoNothing())); - EXPECT_FALSE(executor_.runner()->HasPendingTask()); - EXPECT_TRUE(PostTask(FROM_HERE, {ThreadPool(), MayBlock()}, DoNothing())); EXPECT_FALSE(executor_.runner()->HasPendingTask()); 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 { diff --git a/chromium/base/task/single_thread_task_executor_unittest.cc b/chromium/base/task/single_thread_task_executor_unittest.cc index 3e4d44c24f1..df9162063c5 100644 --- a/chromium/base/task/single_thread_task_executor_unittest.cc +++ b/chromium/base/task/single_thread_task_executor_unittest.cc @@ -1,20 +1,68 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2013 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/single_thread_task_executor.h" +#include <stddef.h> +#include <stdint.h> + +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop_current.h" +#include "base/message_loop/message_pump_for_io.h" +#include "base/message_loop/message_pump_type.h" +#include "base/pending_task.h" +#include "base/posix/eintr_wrapper.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" #include "base/task/post_task.h" +#include "base/task/task_observer.h" +#include "base/task/thread_pool/thread_pool_instance.h" #include "base/test/bind_test_util.h" +#include "base/test/gtest_util.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/test_simple_task_runner.h" +#include "base/test/test_timeouts.h" +#include "base/threading/platform_thread.h" +#include "base/threading/sequence_local_storage_slot.h" +#include "base/threading/thread.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" +#if defined(OS_ANDROID) +#include "base/android/java_handler_thread.h" +#include "base/android/jni_android.h" +#include "base/test/android/java_handler_thread_helpers.h" +#endif + +#if defined(OS_WIN) +#include "base/message_loop/message_pump_win.h" +#include "base/process/memory.h" +#include "base/strings/string16.h" +#include "base/win/current_module.h" +#include "base/win/message_window.h" +#include "base/win/scoped_handle.h" +#endif + using ::testing::IsNull; using ::testing::NotNull; namespace base { +// TODO(darin): Platform-specific MessageLoop tests should be grouped together +// to avoid chopping this file up with so many #ifdefs. + TEST(SingleThreadTaskExecutorTest, GetTaskExecutorForCurrentThread) { EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull()); @@ -43,4 +91,2111 @@ TEST(SingleThreadTaskExecutorTest, run_loop.Run(); } +namespace { + +class Foo : public RefCounted<Foo> { + public: + Foo() : test_count_(0) {} + + void Test0() { ++test_count_; } + + void Test1ConstRef(const std::string& a) { + ++test_count_; + result_.append(a); + } + + void Test1Ptr(std::string* a) { + ++test_count_; + result_.append(*a); + } + + void Test1Int(int a) { test_count_ += a; } + + void Test2Ptr(std::string* a, std::string* b) { + ++test_count_; + result_.append(*a); + result_.append(*b); + } + + void Test2Mixed(const std::string& a, std::string* b) { + ++test_count_; + result_.append(a); + result_.append(*b); + } + + int test_count() const { return test_count_; } + const std::string& result() const { return result_; } + + private: + friend class RefCounted<Foo>; + + ~Foo() = default; + + int test_count_; + std::string result_; + + DISALLOW_COPY_AND_ASSIGN(Foo); +}; + +// This function runs slowly to simulate a large amount of work being done. +static void SlowFunc(TimeDelta pause, int* quit_counter) { + PlatformThread::Sleep(pause); + if (--(*quit_counter) == 0) + RunLoop::QuitCurrentWhenIdleDeprecated(); +} + +// This function records the time when Run was called in a Time object, which is +// useful for building a variety of SingleThreadTaskExecutor tests. +static void RecordRunTimeFunc(TimeTicks* run_time, int* quit_counter) { + *run_time = TimeTicks::Now(); + + // Cause our Run function to take some time to execute. As a result we can + // count on subsequent RecordRunTimeFunc()s running at a future time, + // without worry about the resolution of our system clock being an issue. + SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter); +} + +enum TaskType { + MESSAGEBOX, + ENDDIALOG, + RECURSIVE, + TIMEDMESSAGELOOP, + QUITMESSAGELOOP, + ORDERED, + PUMPS, + SLEEP, + RUNS, +}; + +// Saves the order in which the tasks executed. +struct TaskItem { + TaskItem(TaskType t, int c, bool s) : type(t), cookie(c), start(s) {} + + TaskType type; + int cookie; + bool start; + + bool operator==(const TaskItem& other) const { + return type == other.type && cookie == other.cookie && start == other.start; + } +}; + +std::ostream& operator<<(std::ostream& os, TaskType type) { + switch (type) { + case MESSAGEBOX: + os << "MESSAGEBOX"; + break; + case ENDDIALOG: + os << "ENDDIALOG"; + break; + case RECURSIVE: + os << "RECURSIVE"; + break; + case TIMEDMESSAGELOOP: + os << "TIMEDMESSAGELOOP"; + break; + case QUITMESSAGELOOP: + os << "QUITMESSAGELOOP"; + break; + case ORDERED: + os << "ORDERED"; + break; + case PUMPS: + os << "PUMPS"; + break; + case SLEEP: + os << "SLEEP"; + break; + default: + NOTREACHED(); + os << "Unknown TaskType"; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const TaskItem& item) { + if (item.start) + return os << item.type << " " << item.cookie << " starts"; + return os << item.type << " " << item.cookie << " ends"; +} + +class TaskList { + public: + void RecordStart(TaskType type, int cookie) { + TaskItem item(type, cookie, true); + DVLOG(1) << item; + task_list_.push_back(item); + } + + void RecordEnd(TaskType type, int cookie) { + TaskItem item(type, cookie, false); + DVLOG(1) << item; + task_list_.push_back(item); + } + + size_t Size() { return task_list_.size(); } + + TaskItem Get(int n) { return task_list_[n]; } + + private: + std::vector<TaskItem> task_list_; +}; + +class DummyTaskObserver : public TaskObserver { + public: + explicit DummyTaskObserver(int num_tasks) + : num_tasks_started_(0), num_tasks_processed_(0), num_tasks_(num_tasks) {} + + DummyTaskObserver(int num_tasks, int num_tasks_started) + : num_tasks_started_(num_tasks_started), + num_tasks_processed_(0), + num_tasks_(num_tasks) {} + + ~DummyTaskObserver() override = default; + + void WillProcessTask(const PendingTask& pending_task, + bool /* was_blocked_or_low_priority */) override { + num_tasks_started_++; + EXPECT_LE(num_tasks_started_, num_tasks_); + EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1); + } + + void DidProcessTask(const PendingTask& pending_task) override { + num_tasks_processed_++; + EXPECT_LE(num_tasks_started_, num_tasks_); + EXPECT_EQ(num_tasks_started_, num_tasks_processed_); + } + + int num_tasks_started() const { return num_tasks_started_; } + int num_tasks_processed() const { return num_tasks_processed_; } + + private: + int num_tasks_started_; + int num_tasks_processed_; + const int num_tasks_; + + DISALLOW_COPY_AND_ASSIGN(DummyTaskObserver); +}; + +// A method which reposts itself |depth| times. +void RecursiveFunc(TaskList* order, int cookie, int depth) { + order->RecordStart(RECURSIVE, cookie); + if (depth > 0) { + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&RecursiveFunc, order, cookie, depth - 1)); + } + order->RecordEnd(RECURSIVE, cookie); +} + +void QuitFunc(TaskList* order, int cookie) { + order->RecordStart(QUITMESSAGELOOP, cookie); + RunLoop::QuitCurrentWhenIdleDeprecated(); + order->RecordEnd(QUITMESSAGELOOP, cookie); +} + +void PostNTasks(int posts_remaining) { + if (posts_remaining > 1) { + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&PostNTasks, posts_remaining - 1)); + } +} + +#if defined(OS_WIN) + +void SubPumpFunc(OnceClosure on_done) { + MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop + allow_nestable_tasks; + MSG msg; + while (::GetMessage(&msg, NULL, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + std::move(on_done).Run(); +} + +const wchar_t kMessageBoxTitle[] = L"SingleThreadTaskExecutor Unit Test"; + +// SingleThreadTaskExecutor implicitly start a "modal message loop". Modal +// dialog boxes, common controls (like OpenFile) and StartDoc printing function +// can cause implicit message loops. +void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) { + order->RecordStart(MESSAGEBOX, cookie); + Optional<MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop> + maybe_allow_nesting; + if (is_reentrant) + maybe_allow_nesting.emplace(); + ::MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK); + order->RecordEnd(MESSAGEBOX, cookie); +} + +// Will end the MessageBox. +void EndDialogFunc(TaskList* order, int cookie) { + order->RecordStart(ENDDIALOG, cookie); + HWND window = GetActiveWindow(); + if (window != NULL) { + EXPECT_NE(::EndDialog(window, IDCONTINUE), 0); + // Cheap way to signal that the window wasn't found if RunEnd() isn't + // called. + order->RecordEnd(ENDDIALOG, cookie); + } +} + +// A method which posts a RecursiveFunc that will want to run while +// ::MessageBox() is active. +void RecursiveFuncWin(scoped_refptr<SingleThreadTaskRunner> task_runner, + HANDLE event, + bool expect_window, + TaskList* order, + bool message_box_is_reentrant) { + task_runner->PostTask(FROM_HERE, BindOnce(&RecursiveFunc, order, 1, 2)); + task_runner->PostTask( + FROM_HERE, BindOnce(&MessageBoxFunc, order, 2, message_box_is_reentrant)); + task_runner->PostTask(FROM_HERE, BindOnce(&RecursiveFunc, order, 3, 2)); + // The trick here is that for nested task processing, this task will be + // ran _inside_ the MessageBox message loop, dismissing the MessageBox + // without a chance. + // For non-nested task processing, this will be executed _after_ the + // MessageBox will have been dismissed by the code below, where + // expect_window_ is true. + task_runner->PostTask(FROM_HERE, BindOnce(&EndDialogFunc, order, 4)); + task_runner->PostTask(FROM_HERE, BindOnce(&QuitFunc, order, 5)); + + // Enforce that every tasks are sent before starting to run the main thread + // message loop. + ASSERT_TRUE(SetEvent(event)); + + // Poll for the MessageBox. Don't do this at home! At the speed we do it, + // you will never realize one MessageBox was shown. + for (; expect_window;) { + HWND window = ::FindWindow(L"#32770", kMessageBoxTitle); + if (window) { + // Dismiss it. + for (;;) { + HWND button = ::FindWindowEx(window, NULL, L"Button", NULL); + if (button != NULL) { + EXPECT_EQ(0, ::SendMessage(button, WM_LBUTTONDOWN, 0, 0)); + EXPECT_EQ(0, ::SendMessage(button, WM_LBUTTONUP, 0, 0)); + break; + } + } + break; + } + } +} + +#endif // defined(OS_WIN) + +void PostNTasksThenQuit(int posts_remaining) { + if (posts_remaining > 1) { + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1)); + } else { + RunLoop::QuitCurrentWhenIdleDeprecated(); + } +} + +#if defined(OS_WIN) + +class TestIOHandler : public MessagePumpForIO::IOHandler { + public: + TestIOHandler(const wchar_t* name, HANDLE signal, bool wait); + + void OnIOCompleted(MessagePumpForIO::IOContext* context, + DWORD bytes_transfered, + DWORD error) override; + + void Init(); + void WaitForIO(); + OVERLAPPED* context() { return &context_.overlapped; } + DWORD size() { return sizeof(buffer_); } + + private: + char buffer_[48]; + MessagePumpForIO::IOContext context_; + HANDLE signal_; + win::ScopedHandle file_; + bool wait_; +}; + +TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait) + : MessagePumpForIO::IOHandler(FROM_HERE), signal_(signal), wait_(wait) { + memset(buffer_, 0, sizeof(buffer_)); + + file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL)); + EXPECT_TRUE(file_.IsValid()); +} + +void TestIOHandler::Init() { + MessageLoopCurrentForIO::Get()->RegisterIOHandler(file_.Get(), this); + + DWORD read; + EXPECT_FALSE(ReadFile(file_.Get(), buffer_, size(), &read, context())); + EXPECT_EQ(static_cast<DWORD>(ERROR_IO_PENDING), GetLastError()); + if (wait_) + WaitForIO(); +} + +void TestIOHandler::OnIOCompleted(MessagePumpForIO::IOContext* context, + DWORD bytes_transfered, + DWORD error) { + ASSERT_TRUE(context == &context_); + ASSERT_TRUE(SetEvent(signal_)); +} + +void TestIOHandler::WaitForIO() { + EXPECT_TRUE(MessageLoopCurrentForIO::Get()->WaitForIOCompletion(300, this)); + EXPECT_TRUE(MessageLoopCurrentForIO::Get()->WaitForIOCompletion(400, this)); +} + +void RunTest_IOHandler() { + win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL)); + ASSERT_TRUE(callback_called.IsValid()); + + const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe"; + win::ScopedHandle server( + CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); + ASSERT_TRUE(server.IsValid()); + + Thread thread("IOHandler test"); + Thread::Options options; + options.message_pump_type = MessagePumpType::IO; + ASSERT_TRUE(thread.StartWithOptions(options)); + + TestIOHandler handler(kPipeName, callback_called.Get(), false); + thread.task_runner()->PostTask( + FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler))); + // Make sure the thread runs and sleeps for lack of work. + PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); + + const char buffer[] = "Hello there!"; + DWORD written; + EXPECT_TRUE(WriteFile(server.Get(), buffer, sizeof(buffer), &written, NULL)); + + DWORD result = WaitForSingleObject(callback_called.Get(), 1000); + EXPECT_EQ(WAIT_OBJECT_0, result); + + thread.Stop(); +} + +void RunTest_WaitForIO() { + win::ScopedHandle callback1_called(CreateEvent(NULL, TRUE, FALSE, NULL)); + win::ScopedHandle callback2_called(CreateEvent(NULL, TRUE, FALSE, NULL)); + ASSERT_TRUE(callback1_called.IsValid()); + ASSERT_TRUE(callback2_called.IsValid()); + + const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1"; + const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2"; + win::ScopedHandle server1( + CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); + win::ScopedHandle server2( + CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); + ASSERT_TRUE(server1.IsValid()); + ASSERT_TRUE(server2.IsValid()); + + Thread thread("IOHandler test"); + Thread::Options options; + options.message_pump_type = MessagePumpType::IO; + ASSERT_TRUE(thread.StartWithOptions(options)); + + TestIOHandler handler1(kPipeName1, callback1_called.Get(), false); + TestIOHandler handler2(kPipeName2, callback2_called.Get(), true); + thread.task_runner()->PostTask( + FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler1))); + // TODO(ajwong): Do we really need such long Sleeps in this function? + // Make sure the thread runs and sleeps for lack of work. + TimeDelta delay = TimeDelta::FromMilliseconds(100); + PlatformThread::Sleep(delay); + thread.task_runner()->PostTask( + FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler2))); + PlatformThread::Sleep(delay); + + // At this time handler1 is waiting to be called, and the thread is waiting + // on the Init method of handler2, filtering only handler2 callbacks. + + const char buffer[] = "Hello there!"; + DWORD written; + EXPECT_TRUE(WriteFile(server1.Get(), buffer, sizeof(buffer), &written, NULL)); + PlatformThread::Sleep(2 * delay); + EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), + WaitForSingleObject(callback1_called.Get(), 0)) + << "handler1 has not been called"; + + EXPECT_TRUE(WriteFile(server2.Get(), buffer, sizeof(buffer), &written, NULL)); + + HANDLE objects[2] = {callback1_called.Get(), callback2_called.Get()}; + DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000); + EXPECT_EQ(WAIT_OBJECT_0, result); + + thread.Stop(); +} + +#endif // defined(OS_WIN) + +} // namespace + +//----------------------------------------------------------------------------- +// Each test is run against each type of SingleThreadTaskExecutor. That way we +// are sure that SingleThreadTaskExecutor works properly in all configurations. +// Of course, in some cases, a unit test may only be for a particular type of +// loop. + +class SingleThreadTaskExecutorTypedTest + : public ::testing::TestWithParam<MessagePumpType> { + public: + SingleThreadTaskExecutorTypedTest() = default; + ~SingleThreadTaskExecutorTypedTest() = default; + + static std::string ParamInfoToString( + ::testing::TestParamInfo<MessagePumpType> param_info) { + switch (param_info.param) { + case MessagePumpType::DEFAULT: + return "default_pump"; + case MessagePumpType::IO: + return "IO_pump"; + case MessagePumpType::UI: + return "UI_pump"; + case MessagePumpType::CUSTOM: + break; +#if defined(OS_ANDROID) + case MessagePumpType::JAVA: + break; +#endif // defined(OS_ANDROID) +#if defined(OS_MACOSX) + case MessagePumpType::NS_RUNLOOP: + break; +#endif // defined(OS_MACOSX) +#if defined(OS_WIN) + case MessagePumpType::UI_WITH_WM_QUIT_SUPPORT: + break; +#endif // defined(OS_WIN) + } + NOTREACHED(); + return ""; + } + + private: + DISALLOW_COPY_AND_ASSIGN(SingleThreadTaskExecutorTypedTest); +}; + +TEST_P(SingleThreadTaskExecutorTypedTest, PostTask) { + SingleThreadTaskExecutor executor(GetParam()); + // Add tests to message loop + scoped_refptr<Foo> foo(new Foo()); + std::string a("a"), b("b"), c("c"), d("d"); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&Foo::Test0, foo)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&Foo::Test1Ptr, foo, &b)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&Foo::Test1Int, foo, 100)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d)); + // After all tests, post a message that will shut down the message loop + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated)); + + // Now kick things off + RunLoop().Run(); + + EXPECT_EQ(foo->test_count(), 105); + EXPECT_EQ(foo->result(), "abacad"); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_Basic) { + SingleThreadTaskExecutor executor(GetParam()); + + // Test that PostDelayedTask results in a delayed task. + + const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); + + int num_tasks = 1; + TimeTicks run_time; + + TimeTicks time_before_run = TimeTicks::Now(); + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), kDelay); + RunLoop().Run(); + TimeTicks time_after_run = TimeTicks::Now(); + + EXPECT_EQ(0, num_tasks); + EXPECT_LT(kDelay, time_after_run - time_before_run); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InDelayOrder) { + SingleThreadTaskExecutor executor(GetParam()); + + // Test that two tasks with different delays run in the right order. + int num_tasks = 2; + TimeTicks run_time1, run_time2; + + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), + TimeDelta::FromMilliseconds(200)); + // If we get a large pause in execution (due to a context switch) here, this + // test could fail. + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), + TimeDelta::FromMilliseconds(10)); + + RunLoop().Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time2 < run_time1); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder) { + SingleThreadTaskExecutor executor(GetParam()); + + // Test that two tasks with the same delay run in the order in which they + // were posted. + // + // NOTE: This is actually an approximate test since the API only takes a + // "delay" parameter, so we are not exactly simulating two tasks that get + // posted at the exact same time. It would be nice if the API allowed us to + // specify the desired run time. + + const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); + + int num_tasks = 2; + TimeTicks run_time1, run_time2; + + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay); + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay); + + RunLoop().Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time1 < run_time2); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder_2) { + SingleThreadTaskExecutor executor(GetParam()); + + // Test that a delayed task still runs after a normal tasks even if the + // normal tasks take a long time to run. + + const TimeDelta kPause = TimeDelta::FromMilliseconds(50); + + int num_tasks = 2; + TimeTicks run_time; + + executor.task_runner()->PostTask(FROM_HERE, + BindOnce(&SlowFunc, kPause, &num_tasks)); + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), + TimeDelta::FromMilliseconds(10)); + + TimeTicks time_before_run = TimeTicks::Now(); + RunLoop().Run(); + TimeTicks time_after_run = TimeTicks::Now(); + + EXPECT_EQ(0, num_tasks); + + EXPECT_LT(kPause, time_after_run - time_before_run); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_InPostOrder_3) { + SingleThreadTaskExecutor executor(GetParam()); + + // Test that a delayed task still runs after a pile of normal tasks. The key + // difference between this test and the previous one is that here we return + // the SingleThreadTaskExecutor a lot so we give the SingleThreadTaskExecutor + // plenty of opportunities to maybe run the delayed task. It should know not + // to do so until the delayed task's delay has passed. + + int num_tasks = 11; + TimeTicks run_time1, run_time2; + + // Clutter the ML with tasks. + for (int i = 1; i < num_tasks; ++i) + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks)); + + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), + TimeDelta::FromMilliseconds(1)); + + RunLoop().Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time2 > run_time1); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, PostDelayedTask_SharedTimer) { + SingleThreadTaskExecutor executor(GetParam()); + + // Test that the interval of the timer, used to run the next delayed task, is + // set to a value corresponding to when the next delayed task should run. + + // By setting num_tasks to 1, we ensure that the first task to run causes the + // run loop to exit. + int num_tasks = 1; + TimeTicks run_time1, run_time2; + + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), + TimeDelta::FromSeconds(1000)); + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), + TimeDelta::FromMilliseconds(10)); + + TimeTicks start_time = TimeTicks::Now(); + + RunLoop().Run(); + EXPECT_EQ(0, num_tasks); + + // Ensure that we ran in far less time than the slower timer. + TimeDelta total_time = TimeTicks::Now() - start_time; + EXPECT_GT(5000, total_time.InMilliseconds()); + + // In case both timers somehow run at nearly the same time, sleep a little + // and then run all pending to force them both to have run. This is just + // encouraging flakiness if there is any. + PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(run_time1.is_null()); + EXPECT_FALSE(run_time2.is_null()); +} + +namespace { + +// This is used to inject a test point for recording the destructor calls for +// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we +// are trying to hook the actual destruction, which is not a common operation. +class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> { + public: + RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted) + : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {} + void Run() {} + + private: + friend class RefCounted<RecordDeletionProbe>; + + ~RecordDeletionProbe() { + *was_deleted_ = true; + if (post_on_delete_.get()) + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_)); + } + + scoped_refptr<RecordDeletionProbe> post_on_delete_; + bool* was_deleted_; +}; + +} // namespace + +/* TODO(darin): SingleThreadTaskExecutor does not support deleting all tasks in + */ +/* the destructor. */ +/* Fails, http://crbug.com/50272. */ +TEST_P(SingleThreadTaskExecutorTypedTest, DISABLED_EnsureDeletion) { + bool a_was_deleted = false; + bool b_was_deleted = false; + { + SingleThreadTaskExecutor executor(GetParam()); + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&RecordDeletionProbe::Run, + new RecordDeletionProbe(nullptr, &a_was_deleted))); + // TODO(ajwong): Do we really need 1000ms here? + executor.task_runner()->PostDelayedTask( + FROM_HERE, + BindOnce(&RecordDeletionProbe::Run, + new RecordDeletionProbe(nullptr, &b_was_deleted)), + TimeDelta::FromMilliseconds(1000)); + } + EXPECT_TRUE(a_was_deleted); + EXPECT_TRUE(b_was_deleted); +} + +/* TODO(darin): SingleThreadTaskExecutor does not support deleting all tasks in + */ +/* the destructor. */ +/* Fails, http://crbug.com/50272. */ +TEST_P(SingleThreadTaskExecutorTypedTest, DISABLED_EnsureDeletion_Chain) { + bool a_was_deleted = false; + bool b_was_deleted = false; + bool c_was_deleted = false; + { + SingleThreadTaskExecutor executor(GetParam()); + // The scoped_refptr for each of the below is held either by the chained + // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback. + RecordDeletionProbe* a = new RecordDeletionProbe(nullptr, &a_was_deleted); + RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted); + RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted); + executor.task_runner()->PostTask(FROM_HERE, + BindOnce(&RecordDeletionProbe::Run, c)); + } + EXPECT_TRUE(a_was_deleted); + EXPECT_TRUE(b_was_deleted); + EXPECT_TRUE(c_was_deleted); +} + +namespace { + +void NestingFunc(int* depth) { + if (*depth > 0) { + *depth -= 1; + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&NestingFunc, depth)); + + RunLoop(RunLoop::Type::kNestableTasksAllowed).Run(); + } + base::RunLoop::QuitCurrentWhenIdleDeprecated(); +} + +} // namespace + +TEST_P(SingleThreadTaskExecutorTypedTest, Nesting) { + SingleThreadTaskExecutor executor(GetParam()); + + int depth = 50; + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&NestingFunc, &depth)); + RunLoop().Run(); + EXPECT_EQ(depth, 0); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, Recursive) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&QuitFunc, &order, 3)); + + RunLoop().Run(); + + // FIFO order. + ASSERT_EQ(14U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); + EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false)); +} + +namespace { + +void OrderedFunc(TaskList* order, int cookie) { + order->RecordStart(ORDERED, cookie); + order->RecordEnd(ORDERED, cookie); +} + +} // namespace + +// Tests that non nestable tasks run in FIFO if there are no nested loops. +TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableWithNoNesting) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + ThreadTaskRunnerHandle::Get()->PostNonNestableTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&QuitFunc, &order, 3)); + RunLoop().Run(); + + // FIFO order. + ASSERT_EQ(6U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); +} + +namespace { + +void FuncThatPumps(TaskList* order, int cookie) { + order->RecordStart(PUMPS, cookie); + RunLoop(RunLoop::Type::kNestableTasksAllowed).RunUntilIdle(); + order->RecordEnd(PUMPS, cookie); +} + +void SleepFunc(TaskList* order, int cookie, TimeDelta delay) { + order->RecordStart(SLEEP, cookie); + PlatformThread::Sleep(delay); + order->RecordEnd(SLEEP, cookie); +} + +} // namespace + +// Tests that non nestable tasks don't run when there's code in the call stack. +TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableDelayedInNestedLoop) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&FuncThatPumps, &order, 1)); + ThreadTaskRunnerHandle::Get()->PostNonNestableTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 3)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 5)); + ThreadTaskRunnerHandle::Get()->PostNonNestableTask( + FROM_HERE, BindOnce(&QuitFunc, &order, 6)); + + RunLoop().Run(); + + // FIFO order. + ASSERT_EQ(12U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true)); + EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false)); + EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true)); + EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false)); + EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false)); + EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true)); + EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false)); +} + +namespace { + +void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) { + order->RecordStart(RUNS, cookie); + run_loop->Run(); + order->RecordEnd(RUNS, cookie); +} + +void FuncThatQuitsNow() { + base::RunLoop::QuitCurrentDeprecated(); +} + +} // namespace + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +TEST_P(SingleThreadTaskExecutorTypedTest, QuitNow) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&FuncThatQuitsNow)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 3)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&FuncThatQuitsNow)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 4)); // never runs + + RunLoop().Run(); + + ASSERT_EQ(6U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitTop) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + outer_run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_run_loop.QuitClosure()); + + outer_run_loop.Run(); + + ASSERT_EQ(4U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitNested) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + outer_run_loop.QuitClosure()); + + outer_run_loop.Run(); + + ASSERT_EQ(4U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Quits current loop and immediately runs a nested loop. +void QuitAndRunNestedLoop(TaskList* order, + int cookie, + RunLoop* outer_run_loop, + RunLoop* nested_run_loop) { + order->RecordStart(RUNS, cookie); + outer_run_loop->Quit(); + nested_run_loop->Run(); + order->RecordEnd(RUNS, cookie); +} + +// Test that we can run nested loop after quitting the current one. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopNestedAfterQuit) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop; + + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&QuitAndRunNestedLoop, &order, 1, &outer_run_loop, + &nested_run_loop)); + + outer_run_loop.Run(); + + ASSERT_EQ(2U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitBogus) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + RunLoop bogus_run_loop; + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + bogus_run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + outer_run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_run_loop.QuitClosure()); + + outer_run_loop.Run(); + + ASSERT_EQ(4U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitDeep) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_loop1(RunLoop::Type::kNestableTasksAllowed); + RunLoop nested_loop2(RunLoop::Type::kNestableTasksAllowed); + RunLoop nested_loop3(RunLoop::Type::kNestableTasksAllowed); + RunLoop nested_loop4(RunLoop::Type::kNestableTasksAllowed); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1))); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2))); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3))); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 5)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + outer_run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 6)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_loop1.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 7)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_loop2.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 8)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_loop3.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 9)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + nested_loop4.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 10)); + + outer_run_loop.Run(); + + ASSERT_EQ(18U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit works before RunWithID. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderBefore) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop run_loop; + + run_loop.Quit(); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); // never runs + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs + + run_loop.Run(); + + ASSERT_EQ(0U, order.Size()); +} + +// Tests RunLoopQuit works during RunWithID. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderDuring) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop run_loop; + + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 1)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure()); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); // never runs + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs + + run_loop.Run(); + + ASSERT_EQ(2U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit works after RunWithID. +TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderAfter) { + SingleThreadTaskExecutor executor(GetParam()); + + TaskList order; + + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 2)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&FuncThatQuitsNow)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 3)); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); // has no affect + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&OrderedFunc, &order, 4)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&FuncThatQuitsNow)); + + nested_run_loop.allow_quit_current_deprecated_ = true; + + RunLoop outer_run_loop; + outer_run_loop.Run(); + + ASSERT_EQ(8U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// There was a bug in the MessagePumpGLib where posting tasks recursively +// caused the message loop to hang, due to the buffer of the internal pipe +// becoming full. Test all SingleThreadTaskExecutor types to ensure this issue +// does not exist in other MessagePumps. +// +// On Linux, the pipe buffer size is 64KiB by default. The bug caused one +// byte accumulated in the pipe per two posts, so we should repeat 128K +// times to reproduce the bug. +#if defined(OS_FUCHSIA) +// TODO(crbug.com/810077): This is flaky on Fuchsia. +#define MAYBE_RecursivePosts DISABLED_RecursivePosts +#else +#define MAYBE_RecursivePosts RecursivePosts +#endif +TEST_P(SingleThreadTaskExecutorTypedTest, MAYBE_RecursivePosts) { + const int kNumTimes = 1 << 17; + SingleThreadTaskExecutor executor(GetParam()); + executor.task_runner()->PostTask(FROM_HERE, + BindOnce(&PostNTasksThenQuit, kNumTimes)); + RunLoop().Run(); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksAllowedAtTopLevel) { + SingleThreadTaskExecutor executor(GetParam()); + EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); +} + +// Nestable tasks shouldn't be allowed to run reentrantly by default (regression +// test for https://crbug.com/754112). +TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksDisallowedByDefault) { + SingleThreadTaskExecutor executor(GetParam()); + RunLoop run_loop; + executor.task_runner()->PostTask( + FROM_HERE, + BindOnce( + [](RunLoop* run_loop) { + EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed()); + run_loop->Quit(); + }, + Unretained(&run_loop))); + run_loop.Run(); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, + NestableTasksProcessedWhenRunLoopAllows) { + SingleThreadTaskExecutor executor(GetParam()); + RunLoop run_loop; + executor.task_runner()->PostTask( + FROM_HERE, + BindOnce( + [](RunLoop* run_loop) { + // This test would hang if this RunLoop wasn't of type + // kNestableTasksAllowed (i.e. this is testing that this is + // processed and doesn't hang). + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + BindOnce( + [](RunLoop* nested_run_loop) { + // Each additional layer of application task nesting + // requires its own allowance. The kNestableTasksAllowed + // RunLoop allowed this task to be processed but further + // nestable tasks are by default disallowed from this + // layer. + EXPECT_FALSE( + MessageLoopCurrent::Get()->NestableTasksAllowed()); + nested_run_loop->Quit(); + }, + Unretained(&nested_run_loop))); + nested_run_loop.Run(); + + run_loop->Quit(); + }, + Unretained(&run_loop))); + run_loop.Run(); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, + NestableTasksAllowedExplicitlyInScope) { + SingleThreadTaskExecutor executor(GetParam()); + RunLoop run_loop; + executor.task_runner()->PostTask( + FROM_HERE, + BindOnce( + [](RunLoop* run_loop) { + { + MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop + allow_nestable_tasks; + EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); + } + EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed()); + run_loop->Quit(); + }, + Unretained(&run_loop))); + run_loop.Run(); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTesting) { + SingleThreadTaskExecutor executor(GetParam()); + EXPECT_TRUE(MessageLoopCurrent::Get()->IsIdleForTesting()); + executor.task_runner()->PostTask(FROM_HERE, BindOnce([]() {})); + executor.task_runner()->PostDelayedTask(FROM_HERE, BindOnce([]() {}), + TimeDelta::FromMilliseconds(10)); + EXPECT_FALSE(MessageLoopCurrent::Get()->IsIdleForTesting()); + RunLoop().RunUntilIdle(); + EXPECT_TRUE(MessageLoopCurrent::Get()->IsIdleForTesting()); + + PlatformThread::Sleep(TimeDelta::FromMilliseconds(20)); + EXPECT_TRUE(MessageLoopCurrent::Get()->IsIdleForTesting()); +} + +TEST_P(SingleThreadTaskExecutorTypedTest, IsIdleForTestingNonNestableTask) { + SingleThreadTaskExecutor executor(GetParam()); + RunLoop run_loop; + EXPECT_TRUE(MessageLoopCurrent::Get()->IsIdleForTesting()); + bool nested_task_run = false; + executor.task_runner()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); + + executor.task_runner()->PostNonNestableTask( + FROM_HERE, BindLambdaForTesting([&]() { nested_task_run = true; })); + + executor.task_runner()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_FALSE(nested_task_run); + EXPECT_TRUE(MessageLoopCurrent::Get()->IsIdleForTesting()); + })); + + nested_run_loop.RunUntilIdle(); + EXPECT_FALSE(nested_task_run); + EXPECT_FALSE(MessageLoopCurrent::Get()->IsIdleForTesting()); + })); + + run_loop.RunUntilIdle(); + + EXPECT_TRUE(nested_task_run); + EXPECT_TRUE(MessageLoopCurrent::Get()->IsIdleForTesting()); +} + +INSTANTIATE_TEST_SUITE_P(All, + SingleThreadTaskExecutorTypedTest, + ::testing::Values(MessagePumpType::DEFAULT, + MessagePumpType::UI, + MessagePumpType::IO), + SingleThreadTaskExecutorTypedTest::ParamInfoToString); + +#if defined(OS_WIN) + +// Verifies that the SingleThreadTaskExecutor ignores WM_QUIT, rather than +// quitting. Users of SingleThreadTaskExecutor typically expect to control when +// their RunLoops stop Run()ning explicitly, via QuitClosure() etc (see +// https://crbug.com/720078). +TEST(SingleThreadTaskExecutorTest, WmQuitIsIgnored) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + // Post a WM_QUIT message to the current thread. + ::PostQuitMessage(0); + + // Post a task to the current thread, with a small delay to make it less + // likely that we process the posted task before looking for WM_* messages. + bool task_was_run = false; + RunLoop run_loop; + executor.task_runner()->PostDelayedTask( + FROM_HERE, + BindOnce( + [](bool* flag, OnceClosure closure) { + *flag = true; + std::move(closure).Run(); + }, + &task_was_run, run_loop.QuitClosure()), + TestTimeouts::tiny_timeout()); + + // Run the loop, and ensure that the posted task is processed before we quit. + run_loop.Run(); + EXPECT_TRUE(task_was_run); +} + +TEST(SingleThreadTaskExecutorTest, PostDelayedTask_SharedTimer_SubPump) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + // Test that the interval of the timer, used to run the next delayed task, is + // set to a value corresponding to when the next delayed task should run. + + // By setting num_tasks to 1, we ensure that the first task to run causes the + // run loop to exit. + int num_tasks = 1; + TimeTicks run_time; + + RunLoop run_loop; + + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure())); + + // This very delayed task should never run. + executor.task_runner()->PostDelayedTask( + FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), + TimeDelta::FromSeconds(1000)); + + // This slightly delayed task should run from within SubPumpFunc. + executor.task_runner()->PostDelayedTask(FROM_HERE, + BindOnce(&::PostQuitMessage, 0), + TimeDelta::FromMilliseconds(10)); + + Time start_time = Time::Now(); + + run_loop.Run(); + EXPECT_EQ(1, num_tasks); + + // Ensure that we ran in far less time than the slower timer. + TimeDelta total_time = Time::Now() - start_time; + EXPECT_GT(5000, total_time.InMilliseconds()); + + // In case both timers somehow run at nearly the same time, sleep a little + // and then run all pending to force them both to have run. This is just + // encouraging flakiness if there is any. + PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(run_time.is_null()); +} + +namespace { + +// When this fires (per the associated WM_TIMER firing), it posts an +// application task to quit the native loop. +bool QuitOnSystemTimer(UINT message, + WPARAM wparam, + LPARAM lparam, + LRESULT* result) { + if (message == static_cast<UINT>(WM_TIMER)) { + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + BindOnce(&::PostQuitMessage, 0)); + } + *result = 0; + return true; +} + +// When this fires (per the associated WM_TIMER firing), it posts a delayed +// application task to quit the native loop. +bool DelayedQuitOnSystemTimer(UINT message, + WPARAM wparam, + LPARAM lparam, + LRESULT* result) { + if (message == static_cast<UINT>(WM_TIMER)) { + ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, BindOnce(&::PostQuitMessage, 0), + TimeDelta::FromMilliseconds(10)); + } + *result = 0; + return true; +} + +} // namespace + +// This is a regression test for +// https://crrev.com/c/1455266/9/base/message_loop/message_pump_win.cc#125 +// See below for the delayed task version. +TEST(SingleThreadTaskExecutorTest, PostImmediateTaskFromSystemPump) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + RunLoop run_loop; + + // A native message window to generate a system message which invokes + // QuitOnSystemTimer() when the native timer fires. + win::MessageWindow local_message_window; + local_message_window.Create(BindRepeating(&QuitOnSystemTimer)); + ASSERT_TRUE(::SetTimer(local_message_window.hwnd(), 0, 20, nullptr)); + + // The first task will enter a native message loop. This test then verifies + // that the pump is able to run an immediate application task after the native + // pump went idle. + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure())); + + // Test success is determined by not hanging in this Run() call. + run_loop.Run(); +} + +// This is a regression test for +// https://crrev.com/c/1455266/9/base/message_loop/message_pump_win.cc#125 This +// is the delayed task equivalent of the above PostImmediateTaskFromSystemPump +// test. +// +// As a reminder of how this works, here's the sequence of events in this test: +// 1) Test start: +// work_deduplicator.cc(24): BindToCurrentThread +// work_deduplicator.cc(34): OnWorkRequested +// thread_controller_with_message_pump_impl.cc(237) : DoWork +// work_deduplicator.cc(50): OnWorkStarted +// 2) SubPumpFunc entered: +// message_loop_unittest.cc(278): SubPumpFunc +// 3) ScopedAllowApplicationTasksInNativeNestedLoop triggers nested +// ScheduleWork: work_deduplicator.cc(34): OnWorkRequested +// 4) Nested system loop starts and pumps internal kMsgHaveWork: +// message_loop_unittest.cc(282): SubPumpFunc : Got Message +// message_pump_win.cc(302): HandleWorkMessage +// thread_controller_with_message_pump_impl.cc(237) : DoWork +// 5) Attempt to DoWork(), there's nothing to do, NextWorkInfo indicates delay. +// work_deduplicator.cc(50): OnWorkStarted +// work_deduplicator.cc(58): WillCheckForMoreWork +// work_deduplicator.cc(67): DidCheckForMoreWork +// 6) Return control to HandleWorkMessage() which schedules native timer +// and goes to sleep (no kMsgHaveWork in native queue). +// message_pump_win.cc(328): HandleWorkMessage ScheduleNativeTimer +// 7) Native timer fires and posts the delayed application task: +// message_loop_unittest.cc(282): SubPumpFunc : Got Message +// message_loop_unittest.cc(1581): DelayedQuitOnSystemTimer +// !! This is the critical step verified by this test. Since the +// ThreadController is idle after (6), it won't be invoked again and thus +// won't get a chance to return a NextWorkInfo that indicates the next +// delay. A native timer is thus required to have SubPumpFunc handle it. +// work_deduplicator.cc(42): OnDelayedWorkRequested +// message_pump_win.cc(129): ScheduleDelayedWork +// 9) The scheduled native timer fires and runs application task binding +// ::PostQuitMessage : +// message_loop_unittest.cc(282) SubPumpFunc : Got Message +// work_deduplicator.cc(50): OnWorkStarted +// thread_controller_with_message_pump_impl.cc(237) : DoWork +// 10) SequenceManager updates delay to none and notifies +// (TODO(scheduler-dev): Could remove this step but WorkDeduplicator knows +// to ignore at least): +// work_deduplicator.cc(42): OnDelayedWorkRequested +// 11) Nested application task completes and SubPumpFunc unwinds: +// work_deduplicator.cc(58): WillCheckForMoreWork +// work_deduplicator.cc(67): DidCheckForMoreWork +// 12) ~ScopedAllowApplicationTasksInNativeNestedLoop() makes sure +// WorkDeduplicator knows we're back in DoWork() (not relevant in this test +// but important overall). work_deduplicator.cc(50): OnWorkStarted +// 13) Application task which ran SubPumpFunc completes and test finishes. +// work_deduplicator.cc(67): DidCheckForMoreWork +TEST(SingleThreadTaskExecutorTest, PostDelayedTaskFromSystemPump) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + RunLoop run_loop; + + // A native message window to generate a system message which invokes + // DelayedQuitOnSystemTimer() when the native timer fires. + win::MessageWindow local_message_window; + local_message_window.Create(BindRepeating(&DelayedQuitOnSystemTimer)); + ASSERT_TRUE(::SetTimer(local_message_window.hwnd(), 0, 20, nullptr)); + + // The first task will enter a native message loop. This test then verifies + // that the pump is able to run a delayed application task after the native + // pump went idle. + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure())); + + // Test success is determined by not hanging in this Run() call. + run_loop.Run(); +} + +TEST(SingleThreadTaskExecutorTest, WmQuitIsVisibleToSubPump) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + // Regression test for https://crbug.com/888559. When processing a + // kMsgHaveWork we peek and remove the next message and dispatch that ourself, + // to minimize impact of these messages on message-queue processing. If we + // received kMsgHaveWork dispatched by a nested pump (e.g. ::GetMessage() + // loop) then there is a risk that the next message is that loop's WM_QUIT + // message, which must be processed directly by ::GetMessage() for the loop to + // actually quit. This test verifies that WM_QUIT exits works as expected even + // if it happens to immediately follow a kMsgHaveWork in the queue. + + RunLoop run_loop; + + // This application task will enter the subpump. + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure())); + + // This application task will post a native WM_QUIT. + executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0)); + + // The presence of this application task means that the pump will see a + // non-empty queue after processing the previous application task (which + // posted the WM_QUIT) and hence will repost a kMsgHaveWork message in the + // native event queue. Without the fix to https://crbug.com/888559, this would + // previously result in the subpump processing kMsgHaveWork and it stealing + // the WM_QUIT message, leaving the test hung in the subpump. + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + + // Test success is determined by not hanging in this Run() call. + run_loop.Run(); +} + +TEST(SingleThreadTaskExecutorTest, + RepostingWmQuitDoesntStarveUpcomingNativeLoop) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + // This test ensures that application tasks are being processed by the native + // subpump despite the kMsgHaveWork event having already been consumed by the + // time the subpump is entered. This is subtly enforced by + // MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop which + // will ScheduleWork() upon construction (and if it's absent, the + // SingleThreadTaskExecutor shouldn't process application tasks so + // kMsgHaveWork is irrelevant). Note: This test also fails prior to the fix + // for https://crbug.com/888559 (in fact, the last two tasks are sufficient as + // a regression test), probably because of a dangling kMsgHaveWork recreating + // the effect from + // SingleThreadTaskExecutorTest.NativeMsgProcessingDoesntStealWmQuit. + + RunLoop run_loop; + + // This application task will post a native WM_QUIT which will be ignored + // by the main message pump. + executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0)); + + // Make sure the pump does a few extra cycles and processes (ignores) the + // WM_QUIT. + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + + // This application task will enter the subpump. + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure())); + + // Post an application task that will post WM_QUIT to the nested loop. The + // test will hang if the subpump doesn't process application tasks as it + // should. + executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0)); + + // Test success is determined by not hanging in this Run() call. + run_loop.Run(); +} + +// TODO(https://crbug.com/890016): Enable once multiple layers of nested loops +// works. +TEST(SingleThreadTaskExecutorTest, + DISABLED_UnwindingMultipleSubPumpsDoesntStarveApplicationTasks) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + // Regression test for https://crbug.com/890016. + // Tests that the subpump is still processing application tasks after + // unwinding from nested subpumps (i.e. that they didn't consume the last + // kMsgHaveWork). + + RunLoop run_loop; + + // Enter multiple levels of nested subpumps. + executor.task_runner()->PostTask( + FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure())); + executor.task_runner()->PostTask(FROM_HERE, + BindOnce(&SubPumpFunc, DoNothing::Once())); + executor.task_runner()->PostTask(FROM_HERE, + BindOnce(&SubPumpFunc, DoNothing::Once())); + + // Quit two layers (with tasks in between to allow each quit to be handled + // before continuing -- ::PostQuitMessage() sets a bit, it's not a real queued + // message : + // https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453). + executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0)); + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0)); + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + executor.task_runner()->PostTask(FROM_HERE, DoNothing()); + + bool last_task_ran = false; + executor.task_runner()->PostTask( + FROM_HERE, BindOnce([](bool* to_set) { *to_set = true; }, + Unretained(&last_task_ran))); + + executor.task_runner()->PostTask(FROM_HERE, BindOnce(&::PostQuitMessage, 0)); + + run_loop.Run(); + + EXPECT_TRUE(last_task_ran); +} + +namespace { + +// A side effect of this test is the generation a beep. Sorry. +void RunTest_NestingDenial2(MessagePumpType message_pump_type) { + SingleThreadTaskExecutor executor(message_pump_type); + + Thread worker("NestingDenial2_worker"); + Thread::Options options; + options.message_pump_type = message_pump_type; + ASSERT_EQ(true, worker.StartWithOptions(options)); + TaskList order; + win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); + worker.task_runner()->PostTask( + FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(), + event.Get(), true, &order, false)); + // Let the other thread execute. + WaitForSingleObject(event.Get(), INFINITE); + RunLoop().Run(); + + ASSERT_EQ(17u, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false)); + // When EndDialogFunc is processed, the window is already dismissed, hence no + // "end" entry. + EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true)); + EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true)); + EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false)); + EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false)); + EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false)); +} + +} // namespace + +// This test occasionally hangs, would need to be turned into an +// interactive_ui_test, see http://crbug.com/44567. +TEST(SingleThreadTaskExecutorTest, DISABLED_NestingDenial2) { + RunTest_NestingDenial2(MessagePumpType::DEFAULT); + RunTest_NestingDenial2(MessagePumpType::UI); + RunTest_NestingDenial2(MessagePumpType::IO); +} + +// A side effect of this test is the generation a beep. Sorry. This test also +// needs to process windows messages on the current thread. +TEST(SingleThreadTaskExecutorTest, NestingSupport2) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + + Thread worker("NestingSupport2_worker"); + Thread::Options options; + options.message_pump_type = MessagePumpType::UI; + ASSERT_EQ(true, worker.StartWithOptions(options)); + TaskList order; + win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); + worker.task_runner()->PostTask( + FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(), + event.Get(), false, &order, true)); + // Let the other thread execute. + WaitForSingleObject(event.Get(), INFINITE); + RunLoop().Run(); + + ASSERT_EQ(18u, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true)); + // Note that this executes in the MessageBox modal loop. + EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false)); + EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true)); + EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false)); + EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false)); + /* The order can subtly change here. The reason is that when RecursiveFunc(1) + is called in the main thread, if it is faster than getting to the + PostTask(FROM_HERE, BindOnce(&QuitFunc) execution, the order of task + execution can change. We don't care anyway that the order isn't correct. + EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true)); + EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, false)); + EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); + */ + EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false)); + EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false)); +} + +#endif // defined(OS_WIN) + +#if defined(OS_WIN) +TEST(SingleThreadTaskExecutorTest, IOHandler) { + RunTest_IOHandler(); +} + +TEST(SingleThreadTaskExecutorTest, WaitForIO) { + RunTest_WaitForIO(); +} + +TEST(SingleThreadTaskExecutorTest, HighResolutionTimer) { + SingleThreadTaskExecutor executor; + Time::EnableHighResolutionTimer(true); + + constexpr TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5); + constexpr TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100); + + { + // Post a fast task to enable the high resolution timers. + RunLoop run_loop; + executor.task_runner()->PostDelayedTask( + FROM_HERE, + BindOnce( + [](RunLoop* run_loop) { + EXPECT_TRUE(Time::IsHighResolutionTimerInUse()); + run_loop->QuitWhenIdle(); + }, + &run_loop), + kFastTimer); + run_loop.Run(); + } + EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); + { + // Check that a slow task does not trigger the high resolution logic. + RunLoop run_loop; + executor.task_runner()->PostDelayedTask( + FROM_HERE, + BindOnce( + [](RunLoop* run_loop) { + EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); + run_loop->QuitWhenIdle(); + }, + &run_loop), + kSlowTimer); + run_loop.Run(); + } + Time::EnableHighResolutionTimer(false); + Time::ResetHighResolutionTimerUsage(); +} + +#endif // defined(OS_WIN) + +namespace { +// Inject a test point for recording the destructor calls for Closure objects +// send to MessageLoop::PostTask(). It is awkward usage since we are trying to +// hook the actual destruction, which is not a common operation. +class DestructionObserverProbe : public RefCounted<DestructionObserverProbe> { + public: + DestructionObserverProbe(bool* task_destroyed, + bool* destruction_observer_called) + : task_destroyed_(task_destroyed), + destruction_observer_called_(destruction_observer_called) {} + virtual void Run() { + // This task should never run. + ADD_FAILURE(); + } + + private: + friend class RefCounted<DestructionObserverProbe>; + + virtual ~DestructionObserverProbe() { + EXPECT_FALSE(*destruction_observer_called_); + *task_destroyed_ = true; + } + + bool* task_destroyed_; + bool* destruction_observer_called_; +}; + +class MLDestructionObserver : public MessageLoopCurrent::DestructionObserver { + public: + MLDestructionObserver(bool* task_destroyed, bool* destruction_observer_called) + : task_destroyed_(task_destroyed), + destruction_observer_called_(destruction_observer_called), + task_destroyed_before_message_loop_(false) {} + void WillDestroyCurrentMessageLoop() override { + task_destroyed_before_message_loop_ = *task_destroyed_; + *destruction_observer_called_ = true; + } + bool task_destroyed_before_message_loop() const { + return task_destroyed_before_message_loop_; + } + + private: + bool* task_destroyed_; + bool* destruction_observer_called_; + bool task_destroyed_before_message_loop_; +}; + +} // namespace + +TEST(SingleThreadTaskExecutorTest, DestructionObserverTest) { + // Verify that the destruction observer gets called at the very end (after + // all the pending tasks have been destroyed). + auto executor = std::make_unique<SingleThreadTaskExecutor>(); + const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); + + bool task_destroyed = false; + bool destruction_observer_called = false; + + MLDestructionObserver observer(&task_destroyed, &destruction_observer_called); + MessageLoopCurrent::Get()->AddDestructionObserver(&observer); + executor->task_runner()->PostDelayedTask( + FROM_HERE, + BindOnce(&DestructionObserverProbe::Run, + base::MakeRefCounted<DestructionObserverProbe>( + &task_destroyed, &destruction_observer_called)), + kDelay); + executor.reset(); + EXPECT_TRUE(observer.task_destroyed_before_message_loop()); + // The task should have been destroyed when we deleted the loop. + EXPECT_TRUE(task_destroyed); + EXPECT_TRUE(destruction_observer_called); +} + +// Verify that SingleThreadTaskExecutor sets ThreadMainTaskRunner::current() and +// it posts tasks on that message loop. +TEST(SingleThreadTaskExecutorTest, ThreadMainTaskRunner) { + SingleThreadTaskExecutor executor; + + scoped_refptr<Foo> foo(new Foo()); + std::string a("a"); + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a)); + + // Post quit task; + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated)); + + // Now kick things off + RunLoop().Run(); + + EXPECT_EQ(foo->test_count(), 1); + EXPECT_EQ(foo->result(), "a"); +} + +TEST(SingleThreadTaskExecutorTest, type) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + EXPECT_EQ(executor.type(), MessagePumpType::UI); +} + +#if defined(OS_WIN) +void EmptyFunction() {} + +void PostMultipleTasks() { + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::BindOnce(&EmptyFunction)); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::BindOnce(&EmptyFunction)); +} + +static const int kSignalMsg = WM_USER + 2; + +void PostWindowsMessage(HWND message_hwnd) { + PostMessage(message_hwnd, kSignalMsg, 0, 2); +} + +void EndTest(bool* did_run, HWND hwnd) { + *did_run = true; + PostMessage(hwnd, WM_CLOSE, 0, 0); +} + +int kMyMessageFilterCode = 0x5002; + +LRESULT CALLBACK TestWndProcThunk(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message == WM_CLOSE) + EXPECT_TRUE(DestroyWindow(hwnd)); + if (message != kSignalMsg) + return DefWindowProc(hwnd, message, wparam, lparam); + + switch (lparam) { + case 1: + // First, we post a task that will post multiple no-op tasks to make sure + // that the pump's incoming task queue does not become empty during the + // test. + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&PostMultipleTasks)); + // Next, we post a task that posts a windows message to trigger the second + // stage of the test. + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&PostWindowsMessage, hwnd)); + break; + case 2: + // Since we're about to enter a modal loop, tell the message loop that we + // intend to nest tasks. + MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop + allow_nestable_tasks; + bool did_run = false; + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&EndTest, &did_run, hwnd)); + // Run a nested windows-style message loop and verify that our task runs. + // If it doesn't, then we'll loop here until the test times out. + MSG msg; + while (GetMessage(&msg, 0, 0, 0)) { + if (!CallMsgFilter(&msg, kMyMessageFilterCode)) + DispatchMessage(&msg); + // If this message is a WM_CLOSE, explicitly exit the modal loop. + // Posting a WM_QUIT should handle this, but unfortunately + // MessagePumpWin eats WM_QUIT messages even when running inside a modal + // loop. + if (msg.message == WM_CLOSE) + break; + } + EXPECT_TRUE(did_run); + RunLoop::QuitCurrentWhenIdleDeprecated(); + break; + } + return 0; +} + +TEST(SingleThreadTaskExecutorTest, AlwaysHaveUserMessageWhenNesting) { + SingleThreadTaskExecutor executor(MessagePumpType::UI); + HINSTANCE instance = CURRENT_MODULE(); + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = TestWndProcThunk; + wc.hInstance = instance; + wc.lpszClassName = L"SingleThreadTaskExecutorTest_HWND"; + ATOM atom = RegisterClassEx(&wc); + ASSERT_TRUE(atom); + + HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, + HWND_MESSAGE, 0, instance, 0); + ASSERT_TRUE(message_hwnd) << GetLastError(); + + ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1)); + + RunLoop().Run(); + + ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance)); +} +#endif // defined(OS_WIN) + +// Verify that tasks posted to and code running in the scope of the same +// SingleThreadTaskExecutor access the same SequenceLocalStorage values. +TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageSetGet) { + SingleThreadTaskExecutor executor; + + SequenceLocalStorageSlot<int> slot; + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { slot.emplace(11); })); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { EXPECT_EQ(*slot, 11); })); + + RunLoop().RunUntilIdle(); + EXPECT_EQ(*slot, 11); +} + +// Verify that tasks posted to and code running in different MessageLoops access +// different SequenceLocalStorage values. +TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageDifferentMessageLoops) { + SequenceLocalStorageSlot<int> slot; + + { + SingleThreadTaskExecutor executor; + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { slot.emplace(11); })); + + RunLoop().RunUntilIdle(); + EXPECT_EQ(*slot, 11); + } + + SingleThreadTaskExecutor executor; + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { EXPECT_FALSE(slot); })); + + RunLoop().RunUntilIdle(); + EXPECT_NE(slot.GetOrCreateValue(), 11); +} + +namespace { + +class PostTaskOnDestroy { + public: + PostTaskOnDestroy(int times) : times_remaining_(times) {} + ~PostTaskOnDestroy() { PostTaskWithPostingDestructor(times_remaining_); } + + // Post a task that will repost itself on destruction |times| times. + static void PostTaskWithPostingDestructor(int times) { + if (times > 0) { + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, BindOnce([](std::unique_ptr<PostTaskOnDestroy>) {}, + std::make_unique<PostTaskOnDestroy>(times - 1))); + } + } + + private: + const int times_remaining_; + + DISALLOW_COPY_AND_ASSIGN(PostTaskOnDestroy); +}; + +} // namespace + +// Test that SingleThreadTaskExecutor destruction handles a task's destructor +// posting another task. +TEST(SingleThreadTaskExecutorDestructionTest, + DestroysFineWithPostTaskOnDestroy) { + SingleThreadTaskExecutor executor; + + PostTaskOnDestroy::PostTaskWithPostingDestructor(10); +} + } // namespace base diff --git a/chromium/base/task/task_traits.h b/chromium/base/task/task_traits.h index ac2e3a89a2d..67b8cc23ae6 100644 --- a/chromium/base/task/task_traits.h +++ b/chromium/base/task/task_traits.h @@ -13,7 +13,7 @@ #include <utility> #include "base/base_export.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/task/task_traits_extension.h" #include "base/traits_bag.h" #include "build/build_config.h" diff --git a/chromium/base/task/thread_pool/job_task_source.cc b/chromium/base/task/thread_pool/job_task_source.cc index c86e3e0f118..5ff698c7b78 100644 --- a/chromium/base/task/thread_pool/job_task_source.cc +++ b/chromium/base/task/thread_pool/job_task_source.cc @@ -117,9 +117,7 @@ JobTaskSource::JoinFlag::JoinFlag() = default; JobTaskSource::JoinFlag::~JoinFlag() = default; void JobTaskSource::JoinFlag::SetWaiting() { - const auto previous_value = - value_.exchange(kWaitingForWorkerToYield, std::memory_order_relaxed); - DCHECK(previous_value == kNotWaiting); + value_.store(kWaitingForWorkerToYield, std::memory_order_relaxed); } bool JobTaskSource::JoinFlag::ShouldWorkerYield() { @@ -215,6 +213,7 @@ void JobTaskSource::Cancel(TaskSource::Transaction* transaction) { bool JobTaskSource::WaitForParticipationOpportunity() { CheckedAutoLock auto_lock(lock_); + DCHECK(!join_flag_.IsWaiting()); // std::memory_order_relaxed is sufficient because no other state is // synchronized with |state_| outside of |lock_|. diff --git a/chromium/base/task/thread_pool/job_task_source.h b/chromium/base/task/thread_pool/job_task_source.h index e7e578db590..b043f3269a1 100644 --- a/chromium/base/task/thread_pool/job_task_source.h +++ b/chromium/base/task/thread_pool/job_task_source.h @@ -150,6 +150,12 @@ class BASE_EXPORT JobTaskSource : public TaskSource { JoinFlag(); ~JoinFlag(); + // Returns true if the status is not kNotWaiting, using + // std::memory_order_relaxed. + bool IsWaiting() { + return value_.load(std::memory_order_relaxed) != kNotWaiting; + } + // Sets the status as kWaitingForWorkerToYield using // std::memory_order_relaxed. void SetWaiting(); diff --git a/chromium/base/task/thread_pool/service_thread_unittest.cc b/chromium/base/task/thread_pool/service_thread_unittest.cc index 7b3d3c21031..d5b536a684f 100644 --- a/chromium/base/task/thread_pool/service_thread_unittest.cc +++ b/chromium/base/task/thread_pool/service_thread_unittest.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/debug/stack_trace.h" +#include "base/logging.h" #include "base/task/thread_pool/thread_pool_impl.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/test/metrics/histogram_tester.h" diff --git a/chromium/base/task/thread_pool/task_tracker.cc b/chromium/base/task/thread_pool/task_tracker.cc index 1b02bf0f75a..06311fc3c96 100644 --- a/chromium/base/task/thread_pool/task_tracker.cc +++ b/chromium/base/task/thread_pool/task_tracker.cc @@ -13,10 +13,12 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/json/json_writer.h" +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/optional.h" #include "base/sequence_token.h" +#include "base/strings/string_util.h" #include "base/synchronization/condition_variable.h" #include "base/task/scoped_set_task_priority_for_current_thread.h" #include "base/task/task_executor.h" @@ -25,7 +27,7 @@ #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" #include "base/values.h" #include "build/build_config.h" diff --git a/chromium/base/task/thread_pool/task_tracker.h b/chromium/base/task/thread_pool/task_tracker.h index ab19ad3bb03..eebd7adde42 100644 --- a/chromium/base/task/thread_pool/task_tracker.h +++ b/chromium/base/task/thread_pool/task_tracker.h @@ -14,7 +14,6 @@ #include "base/atomicops.h" #include "base/base_export.h" #include "base/callback_forward.h" -#include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_base.h" #include "base/sequence_checker.h" diff --git a/chromium/base/task/thread_pool/task_tracker_posix.h b/chromium/base/task/thread_pool/task_tracker_posix.h index 8f59d5368f3..c507004932b 100644 --- a/chromium/base/task/thread_pool/task_tracker_posix.h +++ b/chromium/base/task/thread_pool/task_tracker_posix.h @@ -8,7 +8,6 @@ #include <memory> #include "base/base_export.h" -#include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_pump_type.h" #include "base/task/thread_pool/task_tracker.h" diff --git a/chromium/base/task/thread_pool/thread_group_impl.h b/chromium/base/task/thread_pool/thread_group_impl.h index 26f7da99dc3..3431be2f298 100644 --- a/chromium/base/task/thread_pool/thread_group_impl.h +++ b/chromium/base/task/thread_pool/thread_group_impl.h @@ -12,10 +12,10 @@ #include <vector> #include "base/base_export.h" +#include "base/check_op.h" #include "base/compiler_specific.h" #include "base/containers/stack.h" #include "base/gtest_prod_util.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/optional.h" diff --git a/chromium/base/task/thread_pool/thread_pool_impl.h b/chromium/base/task/thread_pool/thread_pool_impl.h index 57a23fd0e0e..b7b4ac73f67 100644 --- a/chromium/base/task/thread_pool/thread_pool_impl.h +++ b/chromium/base/task/thread_pool/thread_pool_impl.h @@ -10,7 +10,7 @@ #include "base/base_export.h" #include "base/callback.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" diff --git a/chromium/base/task/thread_pool/tracked_ref.h b/chromium/base/task/thread_pool/tracked_ref.h index 3b398f1a63e..b36e8e71430 100644 --- a/chromium/base/task/thread_pool/tracked_ref.h +++ b/chromium/base/task/thread_pool/tracked_ref.h @@ -8,8 +8,8 @@ #include <memory> #include "base/atomic_ref_count.h" +#include "base/check.h" #include "base/gtest_prod_util.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/synchronization/waitable_event.h" diff --git a/chromium/base/task/thread_pool/worker_thread.cc b/chromium/base/task/thread_pool/worker_thread.cc index 79e5105b933..429838cf7df 100644 --- a/chromium/base/task/thread_pool/worker_thread.cc +++ b/chromium/base/task/thread_pool/worker_thread.cc @@ -16,7 +16,7 @@ #include "base/task/thread_pool/worker_thread_observer.h" #include "base/threading/hang_watcher.h" #include "base/time/time_override.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" #if defined(OS_MACOSX) #include "base/mac/scoped_nsautorelease_pool.h" @@ -293,9 +293,8 @@ NOINLINE void WorkerThread::RunBackgroundDedicatedCOMWorker() { void WorkerThread::RunWorker() { DCHECK_EQ(self_, this); - TRACE_EVENT_INSTANT0("thread_pool", "WorkerThreadThread born", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_BEGIN0("thread_pool", "WorkerThreadThread active"); + TRACE_EVENT_INSTANT0("base", "WorkerThread born", TRACE_EVENT_SCOPE_THREAD); + TRACE_EVENT_BEGIN0("base", "WorkerThread active"); if (worker_thread_observer_) worker_thread_observer_->OnWorkerThreadMainEntry(); @@ -317,9 +316,9 @@ void WorkerThread::RunWorker() { // A WorkerThread starts out waiting for work. { - TRACE_EVENT_END0("thread_pool", "WorkerThreadThread active"); + TRACE_EVENT_END0("base", "WorkerThread active"); delegate_->WaitForWork(&wake_up_event_); - TRACE_EVENT_BEGIN0("thread_pool", "WorkerThreadThread active"); + TRACE_EVENT_BEGIN0("base", "WorkerThread active"); } while (!ShouldExit()) { @@ -339,10 +338,10 @@ void WorkerThread::RunWorker() { if (ShouldExit()) break; - TRACE_EVENT_END0("thread_pool", "WorkerThreadThread active"); + TRACE_EVENT_END0("base", "WorkerThread active"); hang_watch_scope.reset(); delegate_->WaitForWork(&wake_up_event_); - TRACE_EVENT_BEGIN0("thread_pool", "WorkerThreadThread active"); + TRACE_EVENT_BEGIN0("base", "WorkerThread active"); continue; } @@ -370,9 +369,8 @@ void WorkerThread::RunWorker() { // and as such no more member accesses should be made after this point. self_ = nullptr; - TRACE_EVENT_END0("thread_pool", "WorkerThreadThread active"); - TRACE_EVENT_INSTANT0("thread_pool", "WorkerThreadThread dead", - TRACE_EVENT_SCOPE_THREAD); + TRACE_EVENT_END0("base", "WorkerThread active"); + TRACE_EVENT_INSTANT0("base", "WorkerThread dead", TRACE_EVENT_SCOPE_THREAD); } } // namespace internal |