summaryrefslogtreecommitdiff
path: root/chromium/base/task
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/base/task')
-rw-r--r--chromium/base/task/common/checked_lock.h20
-rw-r--r--chromium/base/task/common/checked_lock_impl.cc13
-rw-r--r--chromium/base/task/common/checked_lock_impl.h6
-rw-r--r--chromium/base/task/common/checked_lock_unittest.cc72
-rw-r--r--chromium/base/task/common/task_annotator.cc16
-rw-r--r--chromium/base/task/post_job.h2
-rw-r--r--chromium/base/task/post_task.cc13
-rw-r--r--chromium/base/task/post_task.h21
-rw-r--r--chromium/base/task/post_task_unittest.cc4
-rw-r--r--chromium/base/task/sequence_manager/lazily_deallocated_deque.h2
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.cc118
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.h22
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc187
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_perftest.cc1
-rw-r--r--chromium/base/task/sequence_manager/sequenced_task_source.h13
-rw-r--r--chromium/base/task/sequence_manager/task_queue_impl.cc140
-rw-r--r--chromium/base/task/sequence_manager/task_queue_impl.h23
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.cc55
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.h24
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector_unittest.cc69
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_impl.cc2
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_power_monitor.cc91
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_power_monitor.h56
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_power_monitor_unittest.cc69
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc43
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h38
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc172
-rw-r--r--chromium/base/task/sequence_manager/time_domain.cc17
-rw-r--r--chromium/base/task/sequence_manager/time_domain.h8
-rw-r--r--chromium/base/task/sequence_manager/time_domain_unittest.cc1
-rw-r--r--chromium/base/task/sequence_manager/work_queue.cc10
-rw-r--r--chromium/base/task/sequence_manager/work_queue.h5
-rw-r--r--chromium/base/task/sequence_manager/work_queue_sets.h4
-rw-r--r--chromium/base/task/single_thread_task_executor_unittest.cc2157
-rw-r--r--chromium/base/task/task_traits.h2
-rw-r--r--chromium/base/task/thread_pool/job_task_source.cc5
-rw-r--r--chromium/base/task/thread_pool/job_task_source.h6
-rw-r--r--chromium/base/task/thread_pool/service_thread_unittest.cc1
-rw-r--r--chromium/base/task/thread_pool/task_tracker.cc4
-rw-r--r--chromium/base/task/thread_pool/task_tracker.h1
-rw-r--r--chromium/base/task/thread_pool/task_tracker_posix.h1
-rw-r--r--chromium/base/task/thread_pool/thread_group_impl.h2
-rw-r--r--chromium/base/task/thread_pool/thread_pool_impl.h2
-rw-r--r--chromium/base/task/thread_pool/tracked_ref.h2
-rw-r--r--chromium/base/task/thread_pool/worker_thread.cc20
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