Commit 0dd49956 authored by Alex Clarke's avatar Alex Clarke Committed by Commit Bot

Implement base::GetContinuationTaskRunner().

This returns to the current task's task runner. Note there are some
situations where this is not valid:

* When we're not running a task (it will DCHECK).
* When we're in a one-off base::ThreadPool task (it will DCHECK).
* In non-scheduler backed threads (it will DCHECK).

Bug: 835323
Change-Id: I3a789557247240e0c36a47339574350d79746144
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1834094
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarRohit Rao <rohitrao@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707484}
parent f9fc312c
...@@ -146,4 +146,17 @@ scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( ...@@ -146,4 +146,17 @@ scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
} }
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner() {
TaskExecutor* executor = GetTaskExecutorForCurrentThread();
DCHECK(executor) << "Couldn't find a TaskExecutor for this thread. Note "
"you can't use base::GetContinuationTaskRunner in "
"a one-off base::ThreadPool task.";
const auto& task_runner = executor->GetContinuationTaskRunner();
DCHECK(task_runner)
<< "The current execution context lacks a continuation task runner. "
"Note: you can't use base::GetContinuationTaskRunner() from a native "
"system event or any other context outside of a Chrome task.";
return task_runner;
}
} // namespace base } // namespace base
...@@ -51,6 +51,13 @@ namespace base { ...@@ -51,6 +51,13 @@ namespace base {
// task_runner->PostTask(FROM_HERE, BindOnce(...)); // task_runner->PostTask(FROM_HERE, BindOnce(...));
// task_runner->PostTask(FROM_HERE, BindOnce(...)); // task_runner->PostTask(FROM_HERE, BindOnce(...));
// //
// To post a task on the current thread or sequence but with an explicit
// priority:
// PostTask(FROM_HERE,
// {CurrentThread(), TaskPriority::BEST_EFFORT},
// BindOnce(...));
//
//
// The default traits apply to tasks that: // The default traits apply to tasks that:
// (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()), // (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()),
// (2) prefer inheriting the current priority to specifying their own, and // (2) prefer inheriting the current priority to specifying their own, and
...@@ -232,6 +239,12 @@ BASE_EXPORT scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( ...@@ -232,6 +239,12 @@ BASE_EXPORT scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
SingleThreadTaskRunnerThreadMode::SHARED); SingleThreadTaskRunnerThreadMode::SHARED);
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
// Returns: The SequencedTaskRunner for the currently executing task, if any.
// Otherwise it returns a null scoped_refptr. On threads where there's no
// TaskExecutor registered this will DCHECK e.g. in a one-off ThreadPool task.
BASE_EXPORT const scoped_refptr<SequencedTaskRunner>&
GetContinuationTaskRunner();
} // namespace base } // namespace base
#endif // BASE_TASK_POST_TASK_H_ #endif // BASE_TASK_POST_TASK_H_
...@@ -73,6 +73,9 @@ class MockTaskExecutor : public TaskExecutor { ...@@ -73,6 +73,9 @@ class MockTaskExecutor : public TaskExecutor {
SingleThreadTaskRunnerThreadMode thread_mode)); SingleThreadTaskRunnerThreadMode thread_mode));
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
MOCK_METHOD0(GetContinuationTaskRunner,
const scoped_refptr<SequencedTaskRunner>&());
TestSimpleTaskRunner* runner() const { return runner_.get(); } TestSimpleTaskRunner* runner() const { return runner_.get(); }
private: private:
...@@ -279,6 +282,49 @@ TEST_F(PostTaskTestWithExecutor, ...@@ -279,6 +282,49 @@ TEST_F(PostTaskTestWithExecutor,
run_loop.Run(); run_loop.Run();
} }
TEST_F(PostTaskTestWithExecutor, TaskRunnerTaskGetContinuationTaskRunner) {
auto task_runner = CreateTaskRunner({ThreadPool()});
RunLoop run_loop;
EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
// GetContinuationTaskRunner is
// meaningless in this context.
EXPECT_DCHECK_DEATH(
GetContinuationTaskRunner());
run_loop.Quit();
})));
run_loop.Run();
}
TEST_F(PostTaskTestWithExecutor,
SequencedTaskRunnerTaskGetContinuationTaskRunner) {
auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()});
RunLoop run_loop;
EXPECT_TRUE(sequenced_task_runner->PostTask(
FROM_HERE, BindLambdaForTesting([&]() {
EXPECT_EQ(GetContinuationTaskRunner(), sequenced_task_runner);
run_loop.Quit();
})));
run_loop.Run();
}
TEST_F(PostTaskTestWithExecutor,
SingleThreadTaskRunnerTaskGetContinuationTaskRunner) {
auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
RunLoop run_loop;
EXPECT_TRUE(single_thread_task_runner->PostTask(
FROM_HERE, BindLambdaForTesting([&]() {
EXPECT_EQ(GetContinuationTaskRunner(), single_thread_task_runner);
run_loop.Quit();
})));
run_loop.Run();
}
TEST_F(PostTaskTestWithExecutor, ThreadPoolCurrentThreadChangePriority) { TEST_F(PostTaskTestWithExecutor, ThreadPoolCurrentThreadChangePriority) {
auto single_thread_task_runner = auto single_thread_task_runner =
CreateSingleThreadTaskRunner({ThreadPool(), TaskPriority::USER_BLOCKING}); CreateSingleThreadTaskRunner({ThreadPool(), TaskPriority::USER_BLOCKING});
......
...@@ -150,7 +150,8 @@ class BASE_EXPORT SequenceManager { ...@@ -150,7 +150,8 @@ class BASE_EXPORT SequenceManager {
// Returns the task runner the current task was posted on. Returns null if no // Returns the task runner the current task was posted on. Returns null if no
// task is currently running. Must be called on the bound thread. // task is currently running. Must be called on the bound thread.
virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0; virtual const scoped_refptr<SequencedTaskRunner>&
GetTaskRunnerForCurrentTask() = 0;
// Finishes the initialization for a SequenceManager created via // Finishes the initialization for a SequenceManager created via
// CreateUnboundSequenceManagerWithPump(). Must not be called in any other // CreateUnboundSequenceManagerWithPump(). Must not be called in any other
......
...@@ -42,6 +42,12 @@ GetTLSSequenceManagerImpl() { ...@@ -42,6 +42,12 @@ GetTLSSequenceManagerImpl() {
return lazy_tls_ptr.get(); return lazy_tls_ptr.get();
} }
const scoped_refptr<SequencedTaskRunner>& GetNullTaskRunner() {
static const base::NoDestructor<scoped_refptr<SequencedTaskRunner>>
null_task_runner;
return *null_task_runner;
}
} // namespace } // namespace
// This controls how big the the initial for // This controls how big the the initial for
...@@ -320,11 +326,11 @@ void SequenceManagerImpl::BindToCurrentThread( ...@@ -320,11 +326,11 @@ void SequenceManagerImpl::BindToCurrentThread(
BindToMessagePump(std::move(pump)); BindToMessagePump(std::move(pump));
} }
scoped_refptr<SequencedTaskRunner> const scoped_refptr<SequencedTaskRunner>&
SequenceManagerImpl::GetTaskRunnerForCurrentTask() { SequenceManagerImpl::GetTaskRunnerForCurrentTask() {
DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
if (main_thread_only().task_execution_stack.empty()) if (main_thread_only().task_execution_stack.empty())
return nullptr; return GetNullTaskRunner();
return main_thread_only() return main_thread_only()
.task_execution_stack.back() .task_execution_stack.back()
.pending_task.task_runner; .pending_task.task_runner;
......
...@@ -102,7 +102,8 @@ class BASE_EXPORT SequenceManagerImpl ...@@ -102,7 +102,8 @@ class BASE_EXPORT SequenceManagerImpl
// SequenceManager implementation: // SequenceManager implementation:
void BindToCurrentThread() override; void BindToCurrentThread() override;
scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() override; const scoped_refptr<SequencedTaskRunner>& GetTaskRunnerForCurrentTask()
override;
void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override; void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override;
void SetObserver(Observer* observer) override; void SetObserver(Observer* observer) override;
void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override; void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
......
...@@ -4,11 +4,16 @@ ...@@ -4,11 +4,16 @@
#include "base/task/simple_task_executor.h" #include "base/task/simple_task_executor.h"
#include "base/task/sequence_manager/sequence_manager.h"
namespace base { namespace base {
SimpleTaskExecutor::SimpleTaskExecutor( SimpleTaskExecutor::SimpleTaskExecutor(
sequence_manager::SequenceManager* sequence_manager,
scoped_refptr<SingleThreadTaskRunner> task_queue) scoped_refptr<SingleThreadTaskRunner> task_queue)
: task_queue_(std::move(task_queue)), : sequence_manager_(sequence_manager),
sequenced_task_queue_(task_queue),
task_queue_(std::move(task_queue)),
previous_task_executor_(GetTaskExecutorForCurrentThread()) { previous_task_executor_(GetTaskExecutorForCurrentThread()) {
DCHECK(task_queue_); DCHECK(task_queue_);
// The TaskExecutor API does not expect nesting, but this can happen in tests // The TaskExecutor API does not expect nesting, but this can happen in tests
...@@ -38,7 +43,7 @@ scoped_refptr<TaskRunner> SimpleTaskExecutor::CreateTaskRunner( ...@@ -38,7 +43,7 @@ scoped_refptr<TaskRunner> SimpleTaskExecutor::CreateTaskRunner(
scoped_refptr<SequencedTaskRunner> scoped_refptr<SequencedTaskRunner>
SimpleTaskExecutor::CreateSequencedTaskRunner(const TaskTraits& traits) { SimpleTaskExecutor::CreateSequencedTaskRunner(const TaskTraits& traits) {
return task_queue_; return sequenced_task_queue_;
} }
scoped_refptr<SingleThreadTaskRunner> scoped_refptr<SingleThreadTaskRunner>
...@@ -59,4 +64,11 @@ SimpleTaskExecutor::CreateCOMSTATaskRunner( ...@@ -59,4 +64,11 @@ SimpleTaskExecutor::CreateCOMSTATaskRunner(
} }
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
const scoped_refptr<SequencedTaskRunner>&
SimpleTaskExecutor::GetContinuationTaskRunner() {
if (sequence_manager_)
return sequence_manager_->GetTaskRunnerForCurrentTask();
return sequenced_task_queue_;
}
} // namespace base } // namespace base
...@@ -9,13 +9,19 @@ ...@@ -9,13 +9,19 @@
#include "build/build_config.h" #include "build/build_config.h"
namespace base { namespace base {
namespace sequence_manager {
class SequenceManager;
} // namespace sequence_manager
// A simple TaskExecutor with exactly one SingleThreadTaskRunner. // A simple TaskExecutor with exactly one SingleThreadTaskRunner.
// Must be instantiated and destroyed on the thread that runs tasks for the // Must be instantiated and destroyed on the thread that runs tasks for the
// SingleThreadTaskRunner. // SingleThreadTaskRunner.
class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor { class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor {
public: public:
explicit SimpleTaskExecutor(scoped_refptr<SingleThreadTaskRunner> task_queue); // If |sequence_manager| is null, GetContinuationTaskRunner will always return
// |task_queue| even if no task is running.
SimpleTaskExecutor(sequence_manager::SequenceManager* sequence_manager,
scoped_refptr<SingleThreadTaskRunner> task_queue);
~SimpleTaskExecutor() override; ~SimpleTaskExecutor() override;
...@@ -39,7 +45,12 @@ class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor { ...@@ -39,7 +45,12 @@ class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor {
SingleThreadTaskRunnerThreadMode thread_mode) override; SingleThreadTaskRunnerThreadMode thread_mode) override;
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
private: const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner()
override;
protected:
sequence_manager::SequenceManager* const sequence_manager_;
const scoped_refptr<SequencedTaskRunner> sequenced_task_queue_;
const scoped_refptr<SingleThreadTaskRunner> task_queue_; const scoped_refptr<SingleThreadTaskRunner> task_queue_;
// In tests there may already be a TaskExecutor registered for the thread, we // In tests there may already be a TaskExecutor registered for the thread, we
......
...@@ -18,7 +18,7 @@ SingleThreadTaskExecutor::SingleThreadTaskExecutor(MessagePumpType type) ...@@ -18,7 +18,7 @@ SingleThreadTaskExecutor::SingleThreadTaskExecutor(MessagePumpType type)
default_task_queue_(sequence_manager_->CreateTaskQueue( default_task_queue_(sequence_manager_->CreateTaskQueue(
sequence_manager::TaskQueue::Spec("default_tq"))), sequence_manager::TaskQueue::Spec("default_tq"))),
type_(type), type_(type),
simple_task_executor_(task_runner()) { simple_task_executor_(sequence_manager_.get(), task_runner()) {
sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner()); sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner());
sequence_manager_->BindToMessagePump(MessagePump::Create(type)); sequence_manager_->BindToMessagePump(MessagePump::Create(type));
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -55,4 +56,24 @@ TEST(SingleThreadTaskExecutorTest, CurrentThread) { ...@@ -55,4 +56,24 @@ TEST(SingleThreadTaskExecutorTest, CurrentThread) {
{base::CurrentThread(), base::TaskPriority::BEST_EFFORT})); {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
} }
TEST(SingleThreadTaskExecutorTest, GetContinuationTaskRunner) {
SingleThreadTaskExecutor single_thread_task_executor;
RunLoop run_loop;
auto task_runner = CreateSingleThreadTaskRunner({CurrentThread()});
task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
EXPECT_EQ(task_runner, GetContinuationTaskRunner());
run_loop.Quit();
}));
run_loop.Run();
}
TEST(SingleThreadTaskExecutorTest, GetCurrentTaskWithNoTaskRunning) {
SingleThreadTaskExecutor single_thread_task_executor;
EXPECT_DCHECK_DEATH(GetContinuationTaskRunner());
}
} // namespace base } // namespace base
...@@ -63,6 +63,11 @@ class BASE_EXPORT TaskExecutor { ...@@ -63,6 +63,11 @@ class BASE_EXPORT TaskExecutor {
const TaskTraits& traits, const TaskTraits& traits,
SingleThreadTaskRunnerThreadMode thread_mode) = 0; SingleThreadTaskRunnerThreadMode thread_mode) = 0;
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
// Returns the sequence the current task was posted on, if any, or null
// otherwise (e.g. for parallel tasks).
virtual const scoped_refptr<SequencedTaskRunner>&
GetContinuationTaskRunner() = 0;
}; };
// Register a TaskExecutor with the //base/task/post_task.h API in the current // Register a TaskExecutor with the //base/task/post_task.h API in the current
......
...@@ -113,18 +113,20 @@ bool HasLogBestEffortTasksSwitch() { ...@@ -113,18 +113,20 @@ bool HasLogBestEffortTasksSwitch() {
switches::kLogBestEffortTasks); switches::kLogBestEffortTasks);
} }
// Needed for PostTaskHere and CurrentThread. This executor lives for the // Needed for GetContinuationTaskRunner and CurrentThread. This executor lives
// duration of a threadpool task invocation. // for the duration of a threadpool task invocation.
class EphemeralTaskExecutor : public TaskExecutor { class EphemeralTaskExecutor : public TaskExecutor {
public: public:
// |sequenced_task_runner| and |single_thread_task_runner| must outlive this // |sequenced_task_runner| and |single_thread_task_runner| must outlive this
// EphemeralTaskExecutor. // EphemeralTaskExecutor. Note |single_thread_task_runner| may be null.
EphemeralTaskExecutor(SequencedTaskRunner* sequenced_task_runner, EphemeralTaskExecutor(
SingleThreadTaskRunner* single_thread_task_runner, scoped_refptr<SequencedTaskRunner> sequenced_task_runner,
const TaskTraits* sequence_traits) SingleThreadTaskRunner* single_thread_task_runner,
: sequenced_task_runner_(sequenced_task_runner), const TaskTraits* sequence_traits)
: sequenced_task_runner_(std::move(sequenced_task_runner)),
single_thread_task_runner_(single_thread_task_runner), single_thread_task_runner_(single_thread_task_runner),
sequence_traits_(sequence_traits) { sequence_traits_(sequence_traits) {
DCHECK(sequenced_task_runner_);
SetTaskExecutorForCurrentThread(this); SetTaskExecutorForCurrentThread(this);
} }
...@@ -170,6 +172,11 @@ class EphemeralTaskExecutor : public TaskExecutor { ...@@ -170,6 +172,11 @@ class EphemeralTaskExecutor : public TaskExecutor {
} }
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner()
override {
return sequenced_task_runner_;
}
private: private:
// Currently ignores |traits.priority()|. // Currently ignores |traits.priority()|.
void CheckTraitsCompatibleWithSequenceTraits(const TaskTraits& traits) { void CheckTraitsCompatibleWithSequenceTraits(const TaskTraits& traits) {
...@@ -186,7 +193,7 @@ class EphemeralTaskExecutor : public TaskExecutor { ...@@ -186,7 +193,7 @@ class EphemeralTaskExecutor : public TaskExecutor {
sequence_traits_->with_base_sync_primitives()); sequence_traits_->with_base_sync_primitives());
} }
SequencedTaskRunner* const sequenced_task_runner_; const scoped_refptr<SequencedTaskRunner> sequenced_task_runner_;
SingleThreadTaskRunner* const single_thread_task_runner_; SingleThreadTaskRunner* const single_thread_task_runner_;
const TaskTraits* const sequence_traits_; const TaskTraits* const sequence_traits_;
}; };
......
...@@ -60,6 +60,12 @@ bool HasDisableBestEffortTasksSwitch() { ...@@ -60,6 +60,12 @@ bool HasDisableBestEffortTasksSwitch() {
switches::kDisableBestEffortTasks); switches::kDisableBestEffortTasks);
} }
const scoped_refptr<SequencedTaskRunner>& GetNullTaskRunner() {
static const NoDestructor<scoped_refptr<SequencedTaskRunner>>
null_task_runner;
return *null_task_runner;
}
} // namespace } // namespace
ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label) ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label)
...@@ -267,6 +273,14 @@ ThreadPoolImpl::CreateUpdateableSequencedTaskRunner(const TaskTraits& traits) { ...@@ -267,6 +273,14 @@ ThreadPoolImpl::CreateUpdateableSequencedTaskRunner(const TaskTraits& traits) {
return MakeRefCounted<PooledSequencedTaskRunner>(new_traits, this); return MakeRefCounted<PooledSequencedTaskRunner>(new_traits, this);
} }
const scoped_refptr<SequencedTaskRunner>&
ThreadPoolImpl::GetContinuationTaskRunner() {
// Default to null for parallel tasks; see task_tracker.cc's
// EphemeralTaskExecutor for how sequenced contexts handle this.
NOTREACHED();
return GetNullTaskRunner();
}
Optional<TimeTicks> ThreadPoolImpl::NextScheduledRunTimeForTesting() const { Optional<TimeTicks> ThreadPoolImpl::NextScheduledRunTimeForTesting() const {
if (task_tracker_->HasIncompleteTaskSourcesForTesting()) if (task_tracker_->HasIncompleteTaskSourcesForTesting())
return TimeTicks::Now(); return TimeTicks::Now();
......
...@@ -98,6 +98,8 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, ...@@ -98,6 +98,8 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance,
const TaskTraits& traits, const TaskTraits& traits,
SingleThreadTaskRunnerThreadMode thread_mode) override; SingleThreadTaskRunnerThreadMode thread_mode) override;
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner()
override;
scoped_refptr<UpdateableSequencedTaskRunner> scoped_refptr<UpdateableSequencedTaskRunner>
CreateUpdateableSequencedTaskRunner(const TaskTraits& traits); CreateUpdateableSequencedTaskRunner(const TaskTraits& traits);
......
...@@ -396,7 +396,8 @@ TaskEnvironment::TaskEnvironment( ...@@ -396,7 +396,8 @@ TaskEnvironment::TaskEnvironment(
.SetTimeDomain(mock_time_domain_.get())); .SetTimeDomain(mock_time_domain_.get()));
task_runner_ = task_queue_->task_runner(); task_runner_ = task_queue_->task_runner();
sequence_manager_->SetDefaultTaskRunner(task_runner_); sequence_manager_->SetDefaultTaskRunner(task_runner_);
simple_task_executor_ = std::make_unique<SimpleTaskExecutor>(task_runner_); simple_task_executor_ = std::make_unique<SimpleTaskExecutor>(
sequence_manager_.get(), task_runner_);
CHECK(base::ThreadTaskRunnerHandle::IsSet()) CHECK(base::ThreadTaskRunnerHandle::IsSet())
<< "ThreadTaskRunnerHandle should've been set now."; << "ThreadTaskRunnerHandle should've been set now.";
CompleteInitialization(); CompleteInitialization();
......
...@@ -35,7 +35,7 @@ namespace test { ...@@ -35,7 +35,7 @@ namespace test {
// This header exposes SingleThreadTaskEnvironment and TaskEnvironment. // This header exposes SingleThreadTaskEnvironment and TaskEnvironment.
// //
// SingleThreadTaskEnvironment enables the following APIs within its scope: // SingleThreadTaskEnvironment enables the following APIs within its scope:
// - (Thread|Sequenced)TaskRunnerHandle on the main thread // - ThreadTaskRunnerHandle & GetContinuationTaskRunner on the main thread
// - RunLoop on the main thread // - RunLoop on the main thread
// //
// TaskEnvironment additionally enables: // TaskEnvironment additionally enables:
...@@ -46,7 +46,7 @@ namespace test { ...@@ -46,7 +46,7 @@ namespace test {
// Tests should prefer SingleThreadTaskEnvironment over TaskEnvironment when the // Tests should prefer SingleThreadTaskEnvironment over TaskEnvironment when the
// former is sufficient. // former is sufficient.
// //
// Tasks posted to the (Thread|Sequenced)TaskRunnerHandle run synchronously when // Tasks posted to the APIs listed above run synchronously when
// RunLoop::Run(UntilIdle) or TaskEnvironment::RunUntilIdle is called on the // RunLoop::Run(UntilIdle) or TaskEnvironment::RunUntilIdle is called on the
// main thread. // main thread.
// //
...@@ -180,7 +180,7 @@ class TaskEnvironment { ...@@ -180,7 +180,7 @@ class TaskEnvironment {
trait_helpers::NotATraitTag()) {} trait_helpers::NotATraitTag()) {}
// Waits until no undelayed ThreadPool tasks remain. Then, unregisters the // Waits until no undelayed ThreadPool tasks remain. Then, unregisters the
// ThreadPoolInstance and the (Thread|Sequenced)TaskRunnerHandle. // ThreadPoolInstance and the APIs listed in the top level comment.
virtual ~TaskEnvironment(); virtual ~TaskEnvironment();
// Returns a TaskRunner that schedules tasks on the main thread. // Returns a TaskRunner that schedules tasks on the main thread.
...@@ -190,11 +190,11 @@ class TaskEnvironment { ...@@ -190,11 +190,11 @@ class TaskEnvironment {
// always return true if called right after RunUntilIdle. // always return true if called right after RunUntilIdle.
bool MainThreadIsIdle() const; bool MainThreadIsIdle() const;
// Runs tasks until both the (Thread|Sequenced)TaskRunnerHandle and the // Runs tasks until both the APIs listed in the top level comment and the
// ThreadPool's non-delayed queues are empty. // ThreadPool's non-delayed queues are empty. While RunUntilIdle() is quite
// While RunUntilIdle() is quite practical and sometimes even necessary -- for // practical and sometimes even necessary -- for example, to flush all tasks
// example, to flush all tasks bound to Unretained() state before destroying // bound to Unretained() state before destroying test members -- it should be
// test members -- it should be used with caution per the following warnings: // used with caution per the following warnings:
// //
// WARNING #1: This may run long (flakily timeout) and even never return! Do // WARNING #1: This may run long (flakily timeout) and even never return! Do
// not use this when repeating tasks such as animated web pages // not use this when repeating tasks such as animated web pages
......
...@@ -1199,5 +1199,23 @@ TEST_F(TaskEnvironmentTest, CurrentThread) { ...@@ -1199,5 +1199,23 @@ TEST_F(TaskEnvironmentTest, CurrentThread) {
run_loop.Run(); run_loop.Run();
} }
TEST_F(TaskEnvironmentTest, GetContinuationTaskRunner) {
SingleThreadTaskEnvironment task_environment;
RunLoop run_loop;
auto task_runner = CreateSingleThreadTaskRunner({CurrentThread()});
task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
EXPECT_EQ(task_runner, GetContinuationTaskRunner());
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(TaskEnvironmentTest, GetContinuationTaskRunnerWithNoTaskRunning) {
SingleThreadTaskEnvironment task_environment;
EXPECT_DCHECK_DEATH(GetContinuationTaskRunner());
}
} // namespace test } // namespace test
} // namespace base } // namespace base
...@@ -21,6 +21,11 @@ class BASE_EXPORT SequencedTaskRunnerHandle { ...@@ -21,6 +21,11 @@ class BASE_EXPORT SequencedTaskRunnerHandle {
// run after the current task is finished and will satisfy a SequenceChecker. // run after the current task is finished and will satisfy a SequenceChecker.
// It should only be called if IsSet() returns true (see the comment there for // It should only be called if IsSet() returns true (see the comment there for
// the requirements). // the requirements).
//
// DEPRECATED: Prefer base::CurrentThread() instead or
// base::GetContinuationTaskRunner() if you need to post a continuation of the
// current task. See threading_and_tasks.md for details.
// TODO(scheduler-dev): Migrate all instances.
static const scoped_refptr<SequencedTaskRunner>& Get() WARN_UNUSED_RESULT; static const scoped_refptr<SequencedTaskRunner>& Get() WARN_UNUSED_RESULT;
// Returns true if one of the following conditions is fulfilled: // Returns true if one of the following conditions is fulfilled:
......
...@@ -87,7 +87,8 @@ class SequenceManagerThreadDelegate : public Thread::Delegate { ...@@ -87,7 +87,8 @@ class SequenceManagerThreadDelegate : public Thread::Delegate {
sequence_manager_->BindToMessagePump( sequence_manager_->BindToMessagePump(
std::move(message_pump_factory_).Run()); std::move(message_pump_factory_).Run());
sequence_manager_->SetTimerSlack(timer_slack); sequence_manager_->SetTimerSlack(timer_slack);
simple_task_executor_.emplace(GetDefaultTaskRunner()); simple_task_executor_.emplace(sequence_manager_.get(),
GetDefaultTaskRunner());
} }
private: private:
......
...@@ -23,6 +23,10 @@ namespace base { ...@@ -23,6 +23,10 @@ namespace base {
class BASE_EXPORT ThreadTaskRunnerHandle { class BASE_EXPORT ThreadTaskRunnerHandle {
public: public:
// Gets the SingleThreadTaskRunner for the current thread. // Gets the SingleThreadTaskRunner for the current thread.
// DEPRECATED: Prefer base::CurrentThread() instead or
// base::GetContinuationTaskRunner() if you need to post a continuation of the
// current task. See threading_and_tasks.md for details.
// TODO(scheduler-dev): Migrate all instances.
static const scoped_refptr<SingleThreadTaskRunner>& Get() WARN_UNUSED_RESULT; static const scoped_refptr<SingleThreadTaskRunner>& Get() WARN_UNUSED_RESULT;
// Returns true if the SingleThreadTaskRunner is already created for // Returns true if the SingleThreadTaskRunner is already created for
......
...@@ -110,4 +110,10 @@ void BrowserIOThreadDelegate::BindToCurrentThread( ...@@ -110,4 +110,10 @@ void BrowserIOThreadDelegate::BindToCurrentThread(
} }
} }
const scoped_refptr<base::SequencedTaskRunner>&
BrowserIOThreadDelegate::GetTaskRunnerForCurrentTask() const {
DCHECK(sequence_manager_);
return sequence_manager_->GetTaskRunnerForCurrentTask();
}
} // namespace content } // namespace content
...@@ -55,6 +55,10 @@ class CONTENT_EXPORT BrowserIOThreadDelegate : public base::Thread::Delegate { ...@@ -55,6 +55,10 @@ class CONTENT_EXPORT BrowserIOThreadDelegate : public base::Thread::Delegate {
scoped_refptr<Handle> GetHandle() { return task_queues_->GetHandle(); } scoped_refptr<Handle> GetHandle() { return task_queues_->GetHandle(); }
// Must be called on the IO thread.
const scoped_refptr<base::SequencedTaskRunner>& GetTaskRunnerForCurrentTask()
const;
private: private:
class TLSMultiplexer; class TLSMultiplexer;
......
...@@ -64,6 +64,12 @@ QueueType GetQueueType(const base::TaskTraits& traits, ...@@ -64,6 +64,12 @@ QueueType GetQueueType(const base::TaskTraits& traits,
} }
} }
const scoped_refptr<base::SequencedTaskRunner>& GetNullTaskRunner() {
static const base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
null_task_runner;
return *null_task_runner;
}
} // namespace } // namespace
BaseBrowserTaskExecutor::BaseBrowserTaskExecutor() = default; BaseBrowserTaskExecutor::BaseBrowserTaskExecutor() = default;
...@@ -397,6 +403,13 @@ BrowserThread::ID BrowserTaskExecutor::GetCurrentThreadID() const { ...@@ -397,6 +403,13 @@ BrowserThread::ID BrowserTaskExecutor::GetCurrentThreadID() const {
return BrowserThread::UI; return BrowserThread::UI;
} }
const scoped_refptr<base::SequencedTaskRunner>&
BrowserTaskExecutor::GetContinuationTaskRunner() {
NOTREACHED()
<< "Should have been routed to UIThreadExecutor or IOThreadExecutor";
return GetNullTaskRunner();
}
BrowserTaskExecutor::UIThreadExecutor::UIThreadExecutor( BrowserTaskExecutor::UIThreadExecutor::UIThreadExecutor(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler) std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler)
: browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)) { : browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)) {
...@@ -428,6 +441,11 @@ BrowserThread::ID BrowserTaskExecutor::UIThreadExecutor::GetCurrentThreadID() ...@@ -428,6 +441,11 @@ BrowserThread::ID BrowserTaskExecutor::UIThreadExecutor::GetCurrentThreadID()
return BrowserThread::UI; return BrowserThread::UI;
} }
const scoped_refptr<base::SequencedTaskRunner>&
BrowserTaskExecutor::UIThreadExecutor::GetContinuationTaskRunner() {
return browser_ui_thread_scheduler_->GetTaskRunnerForCurrentTask();
}
BrowserTaskExecutor::IOThreadExecutor::IOThreadExecutor( BrowserTaskExecutor::IOThreadExecutor::IOThreadExecutor(
std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate) std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate)
: browser_io_thread_delegate_(std::move(browser_io_thread_delegate)) { : browser_io_thread_delegate_(std::move(browser_io_thread_delegate)) {
...@@ -455,4 +473,10 @@ BrowserThread::ID BrowserTaskExecutor::IOThreadExecutor::GetCurrentThreadID() ...@@ -455,4 +473,10 @@ BrowserThread::ID BrowserTaskExecutor::IOThreadExecutor::GetCurrentThreadID()
return BrowserThread::IO; return BrowserThread::IO;
} }
const scoped_refptr<base::SequencedTaskRunner>&
BrowserTaskExecutor::IOThreadExecutor::GetContinuationTaskRunner() {
DCHECK(browser_io_thread_delegate_);
return browser_io_thread_delegate_->GetTaskRunnerForCurrentTask();
}
} // namespace content } // namespace content
...@@ -173,6 +173,10 @@ class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor { ...@@ -173,6 +173,10 @@ class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor {
BrowserTaskQueues::Validator* validator); BrowserTaskQueues::Validator* validator);
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
// base::TaskExecutor implementation.
const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
override;
private: private:
friend class BrowserIOThreadDelegate; friend class BrowserIOThreadDelegate;
friend class BrowserTaskExecutorTest; friend class BrowserTaskExecutorTest;
...@@ -186,6 +190,10 @@ class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor { ...@@ -186,6 +190,10 @@ class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor {
~UIThreadExecutor() override; ~UIThreadExecutor() override;
// base::TaskExecutor implementation.
const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
override;
scoped_refptr<BrowserUIThreadScheduler::Handle> GetUIThreadHandle(); scoped_refptr<BrowserUIThreadScheduler::Handle> GetUIThreadHandle();
void SetIOThreadHandle( void SetIOThreadHandle(
...@@ -209,6 +217,10 @@ class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor { ...@@ -209,6 +217,10 @@ class CONTENT_EXPORT BrowserTaskExecutor : public BaseBrowserTaskExecutor {
~IOThreadExecutor() override; ~IOThreadExecutor() override;
// base::TaskExecutor implementation.
const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
override;
scoped_refptr<BrowserUIThreadScheduler::Handle> GetIOThreadHandle(); scoped_refptr<BrowserUIThreadScheduler::Handle> GetIOThreadHandle();
void SetUIThreadHandle( void SetUIThreadHandle(
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/task/thread_pool/thread_pool_instance.h" #include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
...@@ -132,6 +133,14 @@ class BrowserTaskTraitsMappingTest : public BrowserTaskExecutorTest { ...@@ -132,6 +133,14 @@ class BrowserTaskTraitsMappingTest : public BrowserTaskExecutorTest {
NOTREACHED(); NOTREACHED();
return BrowserThread::UI; return BrowserThread::UI;
} }
const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
override {
return dummy_;
}
private:
scoped_refptr<base::SequencedTaskRunner> dummy_;
}; };
template <BrowserThread::ID ID> template <BrowserThread::ID ID>
...@@ -310,4 +319,35 @@ TEST_F(BrowserTaskExecutorTest, CurrentThreadAndOtherTraits) { ...@@ -310,4 +319,35 @@ TEST_F(BrowserTaskExecutorTest, CurrentThreadAndOtherTraits) {
{base::CurrentThread(), BrowserTaskType::kNavigation})); {base::CurrentThread(), BrowserTaskType::kNavigation}));
} }
TEST_F(BrowserTaskExecutorTest, GetContinuationTaskRunner) {
// Ensure task queue priorities are set.
BrowserTaskExecutor::PostFeatureListSetup();
std::vector<int> order;
base::RunLoop run_loop;
auto task1 = base::BindLambdaForTesting([&]() {
order.push_back(1);
run_loop.Quit();
});
auto task2 = base::BindLambdaForTesting([&]() { order.push_back(2); });
auto task3 = base::BindLambdaForTesting([&]() { order.push_back(3); });
base::PostTask(FROM_HERE, {BrowserThread::UI}, task1);
// Post a bootstrap task whose continuation tasks should run before |task1|.
base::PostTask(
FROM_HERE, {BrowserThread::UI, BrowserTaskType::kBootstrap},
base::BindLambdaForTesting([&]() {
base::GetContinuationTaskRunner()->PostTask(FROM_HERE, task2);
base::GetContinuationTaskRunner()->PostTask(FROM_HERE, task3);
}));
run_loop.Run();
EXPECT_THAT(order, ElementsAre(2, 3, 1));
}
TEST_F(BrowserTaskExecutorTest, GetContinuationTaskRunnerWithNoTaskExecuting) {
EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner());
}
} // namespace content } // namespace content
...@@ -63,7 +63,14 @@ BrowserUIThreadScheduler::BrowserUIThreadScheduler( ...@@ -63,7 +63,14 @@ BrowserUIThreadScheduler::BrowserUIThreadScheduler(
void BrowserUIThreadScheduler::CommonSequenceManagerSetup( void BrowserUIThreadScheduler::CommonSequenceManagerSetup(
base::sequence_manager::SequenceManager* sequence_manager) { base::sequence_manager::SequenceManager* sequence_manager) {
sequence_manager->EnableCrashKeys("ui_scheduler_async_stack"); sequence_manager_ = sequence_manager;
sequence_manager_->EnableCrashKeys("ui_scheduler_async_stack");
}
const scoped_refptr<base::SequencedTaskRunner>&
BrowserUIThreadScheduler::GetTaskRunnerForCurrentTask() const {
DCHECK(sequence_manager_);
return sequence_manager_->GetTaskRunnerForCurrentTask();
} }
} // namespace content } // namespace content
...@@ -41,6 +41,10 @@ class CONTENT_EXPORT BrowserUIThreadScheduler { ...@@ -41,6 +41,10 @@ class CONTENT_EXPORT BrowserUIThreadScheduler {
scoped_refptr<Handle> GetHandle() const { return handle_; } scoped_refptr<Handle> GetHandle() const { return handle_; }
// Must be called on the UI thread.
const scoped_refptr<base::SequencedTaskRunner>& GetTaskRunnerForCurrentTask()
const;
private: private:
friend class BrowserTaskExecutor; friend class BrowserTaskExecutor;
...@@ -56,6 +60,7 @@ class CONTENT_EXPORT BrowserUIThreadScheduler { ...@@ -56,6 +60,7 @@ class CONTENT_EXPORT BrowserUIThreadScheduler {
std::unique_ptr<base::sequence_manager::SequenceManager> std::unique_ptr<base::sequence_manager::SequenceManager>
owned_sequence_manager_; owned_sequence_manager_;
base::sequence_manager::SequenceManager* sequence_manager_ = nullptr;
BrowserTaskQueues task_queues_; BrowserTaskQueues task_queues_;
scoped_refptr<Handle> handle_; scoped_refptr<Handle> handle_;
......
...@@ -11,12 +11,15 @@ ...@@ -11,12 +11,15 @@
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using base::test::TaskEnvironment; using base::test::TaskEnvironment;
using ::testing::IsNull;
using ::testing::NotNull;
namespace content { namespace content {
...@@ -223,4 +226,37 @@ TEST(BrowserTaskEnvironmentTest, CurrentThreadIOWithRealIOThread) { ...@@ -223,4 +226,37 @@ TEST(BrowserTaskEnvironmentTest, CurrentThreadIOWithRealIOThread) {
run_loop.Run(); run_loop.Run();
} }
TEST(BrowserTaskEnvironmentTest, GetCurrentTaskWithNoTaskRunning) {
BrowserTaskEnvironment task_environment;
EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner());
}
TEST(BrowserTaskEnvironmentTest, GetContinuationTaskRunnerUI) {
BrowserTaskEnvironment task_environment;
base::RunLoop run_loop;
auto ui_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::UI});
ui_task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_EQ(ui_task_runner,
base::GetContinuationTaskRunner());
run_loop.Quit();
}));
run_loop.Run();
}
TEST(BrowserTaskEnvironmentTest, GetContinuationTaskRunnerIO) {
BrowserTaskEnvironment task_environment;
base::RunLoop run_loop;
auto io_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::IO});
io_task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_EQ(io_task_runner,
base::GetContinuationTaskRunner());
run_loop.Quit();
}));
run_loop.Run();
}
} // namespace content } // namespace content
...@@ -241,16 +241,47 @@ sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA)); ...@@ -241,16 +241,47 @@ sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA));
sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB)); sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
``` ```
### Posting to the Current Sequence ### Posting to the Current (Virtual) Thread
The preferred way of posting to the current thread is via the
`base::CurrentThread` trait.
```cpp
// The task will run on the current (virtual) thread's default task queue.
base::PostTask(FROM_HERE, {base::CurrentThread()}, base::BindOnce(&Task));
```
You can optionally specify additional traits. This is important because some
threads such as the Browser UI thread, Browser IO thread and the Blink main
thread have multiple task queues funneled onto the same thread and the default
priority may not be suitable your task.
E.g. you can explicitly set the priority:
```cpp
// The task will run on the current (virtual) thread's best effort queue.
// NOTE only the Browser UI and Browser IO threads support task priority (for
// now), other (virtual) threads will silently ignore traits used in combination
// with `base::CurrentThread`.
base::PostTask(FROM_HERE,
{base::CurrentThread(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&Task));
```
The `base::SequencedTaskRunner` to which the current task was posted can be The `base::SequencedTaskRunner` to which the current task was posted can be
obtained via obtained via
[`base::SequencedTaskRunnerHandle::Get()`](https://cs.chromium.org/chromium/src/base/threading/sequenced_task_runner_handle.h). [`base::GetContinuationTaskRunner()`](https://cs.chromium.org/chromium/src/base/task/post_task.h).
On some threads there is only one task runner so the current sequence is the
same as the current thread. That's not the case in the Browser UI, Browser IO or
Blink main threads. Additionally the notion of the current sequence doesn't
exist for parallel threadpool tasks or when there's no task running, and
`base::GetContinuationTaskRunner()` will DCHECK in those circumstances.
*** note *** note
**NOTE:** it is invalid to call `base::SequencedTaskRunnerHandle::Get()` from a **NOTE:** While its invalid to call `base::GetContinuationTaskRunner()` from a
parallel task, but it is valid from a single-threaded task (a parallel task, it is valid from a sequenced or a single-threaded task. I.e. from
`base::SingleThreadTaskRunner` is a `base::SequencedTaskRunner`). a `base::SequencedTaskRunner` or a `base::SingleThreadTaskRunner`.
*** ***
```cpp ```cpp
...@@ -259,10 +290,37 @@ parallel task, but it is valid from a single-threaded task (a ...@@ -259,10 +290,37 @@ parallel task, but it is valid from a single-threaded task (a
// (in particular, it will run after the current task completes). // (in particular, it will run after the current task completes).
// It is also guaranteed that it won’t run concurrently with any // It is also guaranteed that it won’t run concurrently with any
// task posted to that SequencedTaskRunner. // task posted to that SequencedTaskRunner.
base::SequencedTaskRunnerHandle::Get()-> base::GetContinuationTaskRunner()->
PostTask(FROM_HERE, base::BindOnce(&Task)); PostTask(FROM_HERE, base::BindOnce(&Task));
``` ```
You can also obtain the current thread's default task runner via the
`base::CurrentThread` trait, however you can specify additional traits. This is
important because some threads such as the Browser UI thread and the Blink main
thread have multiple task queues funneled onto the same thread and the default
priority may not suit your task. E.g. you can explicitly set the priority:
```cpp
// The task will run on the current (virtual) thread's best effort queue.
// NOTE only the Browser UI and Browser IO threads support task priority, other
// (virtual) threads will silently ignore traits used in combination with
// `base::CurrentThread`.
base::PostTask(FROM_HERE,
{base::CurrentThread(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&Task));
```
If you need to obtain a task runner with these traits you can do so via
`base::CreateSequencedTaskRunner()`.
```cpp
// Tasks posted to |task_runner| will run on the current (virtual) thread's best
// effort queue.
auto task_runner = base::CreateSequencedTaskRunner(
{base::CurrentThread(), base::TaskPriority::BEST_EFFORT});
```
## Using Sequences Instead of Locks ## Using Sequences Instead of Locks
Usage of locks is discouraged in Chrome. Sequences inherently provide Usage of locks is discouraged in Chrome. Sequences inherently provide
...@@ -380,33 +438,6 @@ Remember that we [prefer sequences to physical ...@@ -380,33 +438,6 @@ Remember that we [prefer sequences to physical
threads](#prefer-sequences-to-physical-threads) and that this thus should rarely threads](#prefer-sequences-to-physical-threads) and that this thus should rarely
be necessary. be necessary.
### Posting to the Current Thread
*** note
**IMPORTANT:** To post a task that needs mutual exclusion with the current
sequence of tasks but doesn’t absolutely need to run on the current thread, use
`base::SequencedTaskRunnerHandle::Get()` instead of
`base::ThreadTaskRunnerHandle::Get()` (ref. [Posting to the Current
Sequence](#Posting-to-the-Current-Sequence)). That will better document the
requirements of the posted task and will avoid unnecessarily making your API
thread-affine. In a single-thread task, `base::SequencedTaskRunnerHandle::Get()`
is equivalent to `base::ThreadTaskRunnerHandle::Get()`.
***
To post a task to the current thread, use
[`base::ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h).
```cpp
// The task will run on the current thread in the future.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&Task));
```
*** note
**NOTE:** It is invalid to call `base::ThreadTaskRunnerHandle::Get()` from a parallel
or a sequenced task.
***
## Posting Tasks to a COM Single-Thread Apartment (STA) Thread (Windows) ## Posting Tasks to a COM Single-Thread Apartment (STA) Thread (Windows)
Tasks that need to run on a COM Single-Thread Apartment (STA) thread must be Tasks that need to run on a COM Single-Thread Apartment (STA) thread must be
...@@ -490,6 +521,17 @@ base::PostTask( ...@@ -490,6 +521,17 @@ base::PostTask(
base::PostTask( base::PostTask(
FROM_HERE, {content::BrowserThread::UI}, FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(...)); base::BindOnce(...));
// This task will run on the current virtual thread (sequence).
base::PostTask(
FROM_HERE, {base::CurrentThread()},
base::BindOnce(...));
// This task will run on the current virtual thread (sequence) with best effort
// priority.
base::PostTask(
FROM_HERE, {base::CurrentThread(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(...));
``` ```
## Keeping the Browser Responsive ## Keeping the Browser Responsive
...@@ -771,5 +813,38 @@ class Foo { ...@@ -771,5 +813,38 @@ class Foo {
Note that this still allows removing all layers of plumbing between //chrome and Note that this still allows removing all layers of plumbing between //chrome and
that component since unit tests will use the leaf layer directly. that component since unit tests will use the leaf layer directly.
## Old APIs
The codebase contains several old APIs for retrieving the current thread and
current sequence's task runners. These are being migrated to new APIs and
shouldn't be used in new code.
[`base::ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h)
returns the default task runner for the current thread. All call sites will be
migrated to use base::CurrentThread instead.
```cpp
// The task will run on the current thread in the future.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&Task));
```
[`base::SequencedTaskRunnerHandle::Get()`](https://cs.chromium.org/chromium/src/base/threading/sequenced_task_runner_handle.h)
returns either the thread's default task runner (Browser UI thread, Browser IO
thread, Blink mail thread) or in a sequenced thread pool task it returns the
current sequence. All call sites will be migrated to either use
base::CurrentThread or `base::GetContinuationTaskRunner()` depending on the
callsite.
```cpp
// The task will run after any task that has already been posted
// to the SequencedTaskRunner to which the current task was posted
// (in particular, it will run after the current task completes).
// It is also guaranteed that it won’t run concurrently with any
// task posted to that SequencedTaskRunner.
base::SequencedTaskRunnerHandle::Get()->
PostTask(FROM_HERE, base::BindOnce(&Task));
```
## FAQ ## FAQ
See [Threading and Tasks FAQ](threading_and_tasks_faq.md) for more examples. See [Threading and Tasks FAQ](threading_and_tasks_faq.md) for more examples.
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/no_destructor.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
...@@ -145,6 +146,13 @@ bool PostTaskHelper(WebThread::ID identifier, ...@@ -145,6 +146,13 @@ bool PostTaskHelper(WebThread::ID identifier,
return accepting_tasks; return accepting_tasks;
} }
const scoped_refptr<base::SequencedTaskRunner>& GetNullTaskRunner() {
static const base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
null_task_runner;
return *null_task_runner;
}
// Task executor for UI and IO threads.
class WebThreadTaskExecutor : public base::TaskExecutor { class WebThreadTaskExecutor : public base::TaskExecutor {
public: public:
WebThreadTaskExecutor() {} WebThreadTaskExecutor() {}
...@@ -178,12 +186,22 @@ class WebThreadTaskExecutor : public base::TaskExecutor { ...@@ -178,12 +186,22 @@ class WebThreadTaskExecutor : public base::TaskExecutor {
return GetTaskRunnerForThread(GetWebThreadIdentifier(traits)); return GetTaskRunnerForThread(GetWebThreadIdentifier(traits));
} }
const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
override {
NOTREACHED() << "WebThreadTaskExecutor isn't registered via "
"base::SetTaskExecutorForCurrentThread";
return GetNullTaskRunner();
}
private: private:
WebThread::ID GetWebThreadIdentifier(const base::TaskTraits& traits) { WebThread::ID GetWebThreadIdentifier(const base::TaskTraits& traits) {
DCHECK_EQ(traits.extension_id(), WebTaskTraitsExtension::kExtensionId); DCHECK_EQ(traits.extension_id(), WebTaskTraitsExtension::kExtensionId);
WebThread::ID id = WebThread::ID id =
traits.GetExtension<WebTaskTraitsExtension>().web_thread(); traits.GetExtension<WebTaskTraitsExtension>().web_thread();
DCHECK_LT(id, WebThread::ID_COUNT); DCHECK_LT(id, WebThread::ID_COUNT);
DCHECK(!traits.use_current_thread())
<< "WebThreadTaskExecutor isn't registered via "
"base::SetTaskExecutorForCurrentThread";
// TODO(crbug.com/872372): Support shutdown behavior on UI/IO threads. // TODO(crbug.com/872372): Support shutdown behavior on UI/IO threads.
if (traits.shutdown_behavior_set_explicitly()) { if (traits.shutdown_behavior_set_explicitly()) {
...@@ -218,6 +236,13 @@ WebThreadImpl::WebThreadImpl( ...@@ -218,6 +236,13 @@ WebThreadImpl::WebThreadImpl(
: identifier_(identifier) { : identifier_(identifier) {
DCHECK(task_runner); DCHECK(task_runner);
if (identifier == WebThread::UI) {
DCHECK(task_runner->BelongsToCurrentThread());
// TODO(scheduler-dev): Pass the backing SequenceManager in here to ensure
// GetContinuationTaskRunner DCHECKS when there's no task running.
ui_thread_tls_executor_.emplace(nullptr, task_runner);
}
WebThreadGlobals& globals = g_globals.Get(); WebThreadGlobals& globals = g_globals.Get();
base::AutoLock lock(globals.lock); base::AutoLock lock(globals.lock);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/task/simple_task_executor.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "ios/web/public/thread/web_thread.h" #include "ios/web/public/thread/web_thread.h"
...@@ -65,6 +66,10 @@ class WebThreadImpl : public WebThread { ...@@ -65,6 +66,10 @@ class WebThreadImpl : public WebThread {
// The identifier of this thread. Only one thread can exist with a given // The identifier of this thread. Only one thread can exist with a given
// identifier at a given time. // identifier at a given time.
ID identifier_; ID identifier_;
// Here to support base::CurrentThread and base::GetContinuationTaskRunner on
// the UI thread.
base::Optional<base::SimpleTaskExecutor> ui_thread_tls_executor_;
}; };
} // namespace web } // namespace web
......
...@@ -6,12 +6,16 @@ ...@@ -6,12 +6,16 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "ios/web/public/test/web_task_environment.h" #include "ios/web/public/test/web_task_environment.h"
#include "ios/web/public/thread/web_task_traits.h" #include "ios/web/public/thread/web_task_traits.h"
#include "ios/web/web_thread_impl.h" #include "ios/web/web_thread_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h" #include "testing/platform_test.h"
using testing::NotNull;
namespace web { namespace web {
class WebThreadTest : public PlatformTest { class WebThreadTest : public PlatformTest {
...@@ -64,4 +68,37 @@ TEST_F(WebThreadTest, PostTaskViaSingleThreadTaskRunner) { ...@@ -64,4 +68,37 @@ TEST_F(WebThreadTest, PostTaskViaSingleThreadTaskRunner) {
run_loop.Run(); run_loop.Run();
} }
TEST_F(WebThreadTest, CurrentThread) {
base::RunLoop run_loop;
base::PostTask(
FROM_HERE, {base::CurrentThread()}, base::BindLambdaForTesting([&]() {
PostTask(FROM_HERE, {base::CurrentThread()}, run_loop.QuitClosure());
}));
run_loop.Run();
}
TEST_F(WebThreadTest, GetContinuationTaskRunner) {
base::RunLoop run_loop;
auto task_runner =
base::CreateSingleThreadTaskRunner({base::CurrentThread()});
task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_EQ(task_runner,
base::GetContinuationTaskRunner());
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(WebThreadTest, GetContinuationTaskRunnerWithNoTaskRunning) {
// TODO(scheduler-dev): GetContinuationTaskRunner should DCHECK if there's no
// task running.
EXPECT_EQ(base::CreateSequencedTaskRunner({base::CurrentThread()}),
base::GetContinuationTaskRunner());
EXPECT_THAT(base::GetContinuationTaskRunner().get(), NotNull());
}
} // namespace web } // namespace web
...@@ -41,7 +41,7 @@ void SchedulerHelper::InitDefaultQueues( ...@@ -41,7 +41,7 @@ void SchedulerHelper::InitDefaultQueues(
DCHECK(sequence_manager_); DCHECK(sequence_manager_);
sequence_manager_->SetDefaultTaskRunner(default_task_runner_); sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
simple_task_executor_.emplace(default_task_runner_); blink_task_executor_.emplace(default_task_runner_, sequence_manager_);
} }
SchedulerHelper::~SchedulerHelper() { SchedulerHelper::~SchedulerHelper() {
...@@ -182,5 +182,18 @@ bool SchedulerHelper::HasCPUTimingForEachTask() const { ...@@ -182,5 +182,18 @@ bool SchedulerHelper::HasCPUTimingForEachTask() const {
return false; return false;
} }
SchedulerHelper::BlinkTaskExecutor::BlinkTaskExecutor(
scoped_refptr<base::SingleThreadTaskRunner> default_task_queue,
base::sequence_manager::SequenceManager* sequence_manager)
: base::SimpleTaskExecutor(sequence_manager, std::move(default_task_queue)),
sequence_manager_(sequence_manager) {}
SchedulerHelper::BlinkTaskExecutor::~BlinkTaskExecutor() = default;
const scoped_refptr<base::SequencedTaskRunner>&
SchedulerHelper::BlinkTaskExecutor::GetContinuationTaskRunner() {
return sequence_manager_->GetTaskRunnerForCurrentTask();
}
} // namespace scheduler } // namespace scheduler
} // namespace blink } // namespace blink
...@@ -130,12 +130,29 @@ class PLATFORM_EXPORT SchedulerHelper ...@@ -130,12 +130,29 @@ class PLATFORM_EXPORT SchedulerHelper
private: private:
friend class SchedulerHelperTest; friend class SchedulerHelperTest;
// Like SimpleTaskExecutor except it knows how to get the current task runner
// from the SequenceManager to implement GetContinuationTaskRunner.
class BlinkTaskExecutor : public base::SimpleTaskExecutor {
public:
BlinkTaskExecutor(
scoped_refptr<base::SingleThreadTaskRunner> default_task_queue,
base::sequence_manager::SequenceManager* sequence_manager);
~BlinkTaskExecutor() override;
// base::TaskExecutor implementation.
const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
override;
private:
base::sequence_manager::SequenceManager* sequence_manager_; // NOT OWNED
};
scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
Observer* observer_; // NOT OWNED Observer* observer_; // NOT OWNED
UkmTaskSampler ukm_task_sampler_; UkmTaskSampler ukm_task_sampler_;
base::Optional<base::SimpleTaskExecutor> simple_task_executor_; base::Optional<BlinkTaskExecutor> blink_task_executor_;
DISALLOW_COPY_AND_ASSIGN(SchedulerHelper); DISALLOW_COPY_AND_ASSIGN(SchedulerHelper);
}; };
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "base/task/sequence_manager/test/sequence_manager_for_test.h" #include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/task/task_executor.h" #include "base/task/task_executor.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h" #include "base/test/simple_test_tick_clock.h"
...@@ -46,6 +47,7 @@ namespace scheduler { ...@@ -46,6 +47,7 @@ namespace scheduler {
// To avoid symbol collisions in jumbo builds. // To avoid symbol collisions in jumbo builds.
namespace main_thread_scheduler_impl_unittest { namespace main_thread_scheduler_impl_unittest {
using testing::IsNull;
using testing::Mock; using testing::Mock;
using testing::NotNull; using testing::NotNull;
using InputEventState = WebThreadScheduler::InputEventState; using InputEventState = WebThreadScheduler::InputEventState;
...@@ -2135,6 +2137,25 @@ TEST_P(MainThreadSchedulerImplTest, CurrentThread) { ...@@ -2135,6 +2137,25 @@ TEST_P(MainThreadSchedulerImplTest, CurrentThread) {
{base::CurrentThread(), base::TaskPriority::BEST_EFFORT})); {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
} }
TEST_P(MainThreadSchedulerImplTest, GetContinuationTaskRunner) {
scoped_refptr<MainThreadTaskQueue> timer_tq = scheduler_->NewTimerTaskQueue(
MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
auto task_runner = timer_tq->CreateTaskRunner(TaskType::kJavascriptTimer);
base::RunLoop run_loop;
task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_EQ(task_runner,
base::GetContinuationTaskRunner());
run_loop.Quit();
}));
run_loop.Run();
}
TEST_P(MainThreadSchedulerImplTest,
GetContinuationTaskRunnerWithNoTaskRunning) {
EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner());
}
class MainThreadSchedulerImplWithMessageLoopTest class MainThreadSchedulerImplWithMessageLoopTest
: public MainThreadSchedulerImplTest { : public MainThreadSchedulerImplTest {
public: public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment