Commit 19509c12 authored by fdoray's avatar fdoray Committed by Commit bot

TaskScheduler: Add TaskTraits::WithWait().

WithWait() tasks are allowed to wait on other things than file I/O. In
particular, they may wait on a WaitableEvent or a ConditionVariable, join a
thread or a process, or make a blocking system call that doesn't involve
interactions with the file system.

In upcoming CLs, this trait will be used in scheduling decisions (more
details on the bug).

BUG=669247

Review-Url: https://codereview.chromium.org/2531663003
Cr-Commit-Position: refs/heads/master@{#435054}
parent 65594e01
...@@ -115,13 +115,15 @@ class TaskSchedulerWorkerPoolImplTest ...@@ -115,13 +115,15 @@ class TaskSchedulerWorkerPoolImplTest
scoped_refptr<TaskRunner> CreateTaskRunnerWithExecutionMode( scoped_refptr<TaskRunner> CreateTaskRunnerWithExecutionMode(
SchedulerWorkerPoolImpl* worker_pool, SchedulerWorkerPoolImpl* worker_pool,
test::ExecutionMode execution_mode) { test::ExecutionMode execution_mode) {
// Allow tasks posted to the returned TaskRunner to wait on a WaitableEvent.
const TaskTraits traits = TaskTraits().WithWait();
switch (execution_mode) { switch (execution_mode) {
case test::ExecutionMode::PARALLEL: case test::ExecutionMode::PARALLEL:
return worker_pool->CreateTaskRunnerWithTraits(TaskTraits()); return worker_pool->CreateTaskRunnerWithTraits(traits);
case test::ExecutionMode::SEQUENCED: case test::ExecutionMode::SEQUENCED:
return worker_pool->CreateSequencedTaskRunnerWithTraits(TaskTraits()); return worker_pool->CreateSequencedTaskRunnerWithTraits(traits);
case test::ExecutionMode::SINGLE_THREADED: case test::ExecutionMode::SINGLE_THREADED:
return worker_pool->CreateSingleThreadTaskRunnerWithTraits(TaskTraits()); return worker_pool->CreateSingleThreadTaskRunnerWithTraits(traits);
} }
ADD_FAILURE() << "Unknown ExecutionMode"; ADD_FAILURE() << "Unknown ExecutionMode";
return nullptr; return nullptr;
...@@ -603,7 +605,7 @@ TEST_F(TaskSchedulerWorkerPoolCheckTlsReuse, CheckDetachedThreads) { ...@@ -603,7 +605,7 @@ TEST_F(TaskSchedulerWorkerPoolCheckTlsReuse, CheckDetachedThreads) {
std::vector<std::unique_ptr<test::TestTaskFactory>> factories; std::vector<std::unique_ptr<test::TestTaskFactory>> factories;
for (size_t i = 0; i < kNumWorkersInWorkerPool; ++i) { for (size_t i = 0; i < kNumWorkersInWorkerPool; ++i) {
factories.push_back(MakeUnique<test::TestTaskFactory>( factories.push_back(MakeUnique<test::TestTaskFactory>(
worker_pool_->CreateTaskRunnerWithTraits(TaskTraits()), worker_pool_->CreateTaskRunnerWithTraits(TaskTraits().WithWait()),
test::ExecutionMode::PARALLEL)); test::ExecutionMode::PARALLEL));
ASSERT_TRUE(factories.back()->PostTask( ASSERT_TRUE(factories.back()->PostTask(
PostNestedTask::NO, PostNestedTask::NO,
...@@ -674,8 +676,8 @@ TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaits) { ...@@ -674,8 +676,8 @@ TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaits) {
WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED); WaitableEvent::InitialState::NOT_SIGNALED);
InitializeWorkerPool(TimeDelta::Max(), kNumWorkersInWorkerPool); InitializeWorkerPool(TimeDelta::Max(), kNumWorkersInWorkerPool);
auto task_runner = auto task_runner = worker_pool_->CreateSequencedTaskRunnerWithTraits(
worker_pool_->CreateSequencedTaskRunnerWithTraits(TaskTraits()); TaskTraits().WithWait());
// Post a task. // Post a task.
task_runner->PostTask(FROM_HERE, task_runner->PostTask(FROM_HERE,
...@@ -718,7 +720,8 @@ TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaitsWithDetach) { ...@@ -718,7 +720,8 @@ TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaitsWithDetach) {
WaitableEvent tasks_can_exit_event(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent tasks_can_exit_event(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED); WaitableEvent::InitialState::NOT_SIGNALED);
InitializeWorkerPool(kReclaimTimeForDetachTests, kNumWorkersInWorkerPool); InitializeWorkerPool(kReclaimTimeForDetachTests, kNumWorkersInWorkerPool);
auto task_runner = worker_pool_->CreateTaskRunnerWithTraits(TaskTraits()); auto task_runner =
worker_pool_->CreateTaskRunnerWithTraits(TaskTraits().WithWait());
// Post tasks to saturate the pool. // Post tasks to saturate the pool.
std::vector<std::unique_ptr<WaitableEvent>> task_started_events; std::vector<std::unique_ptr<WaitableEvent>> task_started_events;
......
...@@ -226,6 +226,9 @@ bool TaskTracker::RunTask(std::unique_ptr<Task> task, ...@@ -226,6 +226,9 @@ bool TaskTracker::RunTask(std::unique_ptr<Task> task,
task->traits.shutdown_behavior() != task->traits.shutdown_behavior() !=
TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN); TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN);
const bool previous_wait_allowed =
ThreadRestrictions::SetWaitAllowed(task->traits.with_wait());
{ {
// Set up SequenceToken as expected for the scope of the task. // Set up SequenceToken as expected for the scope of the task.
ScopedSetSequenceTokenForCurrentThread ScopedSetSequenceTokenForCurrentThread
...@@ -261,6 +264,8 @@ bool TaskTracker::RunTask(std::unique_ptr<Task> task, ...@@ -261,6 +264,8 @@ bool TaskTracker::RunTask(std::unique_ptr<Task> task,
PerformRunTask(std::move(task)); PerformRunTask(std::move(task));
} }
ThreadRestrictions::SetWaitAllowed(previous_wait_allowed);
AfterRunTask(shutdown_behavior); AfterRunTask(shutdown_behavior);
} }
......
...@@ -264,7 +264,7 @@ TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunLongTaskBeforeShutdown) { ...@@ -264,7 +264,7 @@ TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunLongTaskBeforeShutdown) {
WaitableEvent::InitialState::NOT_SIGNALED); WaitableEvent::InitialState::NOT_SIGNALED);
auto blocked_task = base::MakeUnique<Task>( auto blocked_task = base::MakeUnique<Task>(
FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event)), FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event)),
TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()); TaskTraits().WithWait().WithShutdownBehavior(GetParam()), TimeDelta());
// Inform |task_tracker_| that |blocked_task| will be posted. // Inform |task_tracker_| that |blocked_task| will be posted.
EXPECT_TRUE(tracker_.WillPostTask(blocked_task.get())); EXPECT_TRUE(tracker_.WillPostTask(blocked_task.get()));
...@@ -804,5 +804,54 @@ TEST_F(TaskSchedulerTaskTrackerTest, LoadWillPostAndRunDuringShutdown) { ...@@ -804,5 +804,54 @@ TEST_F(TaskSchedulerTaskTrackerTest, LoadWillPostAndRunDuringShutdown) {
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED(); WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
} }
namespace {
class WaitAllowedTestThread : public SimpleThread {
public:
WaitAllowedTestThread() : SimpleThread("WaitAllowedTestThread") {}
private:
void Run() override {
TaskTracker tracker;
// Waiting is allowed by default. Expect TaskTracker to disallow it before
// running a task without the WithWait() trait.
ThreadRestrictions::AssertWaitAllowed();
auto task_without_wait = MakeUnique<Task>(
FROM_HERE, Bind([]() {
EXPECT_DCHECK_DEATH({ ThreadRestrictions::AssertWaitAllowed(); });
}),
TaskTraits(), TimeDelta());
EXPECT_TRUE(tracker.WillPostTask(task_without_wait.get()));
tracker.RunTask(std::move(task_without_wait), SequenceToken::Create());
// Disallow waiting. Expect TaskTracker to allow it before running a task
// with the WithWait() trait.
ThreadRestrictions::DisallowWaiting();
auto task_with_wait =
MakeUnique<Task>(FROM_HERE, Bind([]() {
// Shouldn't fail.
ThreadRestrictions::AssertWaitAllowed();
}),
TaskTraits().WithWait(), TimeDelta());
EXPECT_TRUE(tracker.WillPostTask(task_with_wait.get()));
tracker.RunTask(std::move(task_with_wait), SequenceToken::Create());
}
DISALLOW_COPY_AND_ASSIGN(WaitAllowedTestThread);
};
} // namespace
// Verify that AssertIOAllowed() succeeds for a WithWait() task.
TEST(TaskSchedulerTaskTrackerWaitAllowedTest, WaitAllowed) {
// Run the test on the separate thread since it is not possible to reset the
// "wait allowed" bit of a thread without being a friend of
// ThreadRestrictions.
WaitAllowedTestThread wait_allowed_test_thread;
wait_allowed_test_thread.Start();
wait_allowed_test_thread.Join();
}
} // namespace internal } // namespace internal
} // namespace base } // namespace base
...@@ -17,6 +17,7 @@ namespace base { ...@@ -17,6 +17,7 @@ namespace base {
// request defaults if the behavior is critical to the task. // request defaults if the behavior is critical to the task.
TaskTraits::TaskTraits() TaskTraits::TaskTraits()
: with_file_io_(false), : with_file_io_(false),
with_wait_(false),
priority_(TaskPriority::BACKGROUND), priority_(TaskPriority::BACKGROUND),
shutdown_behavior_(TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {} shutdown_behavior_(TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {}
...@@ -27,6 +28,11 @@ TaskTraits& TaskTraits::WithFileIO() { ...@@ -27,6 +28,11 @@ TaskTraits& TaskTraits::WithFileIO() {
return *this; return *this;
} }
TaskTraits& TaskTraits::WithWait() {
with_wait_ = true;
return *this;
}
TaskTraits& TaskTraits::WithPriority(TaskPriority priority) { TaskTraits& TaskTraits::WithPriority(TaskPriority priority) {
priority_ = priority; priority_ = priority;
return *this; return *this;
......
...@@ -89,18 +89,28 @@ class BASE_EXPORT TaskTraits { ...@@ -89,18 +89,28 @@ class BASE_EXPORT TaskTraits {
TaskTraits& operator=(const TaskTraits& other) = default; TaskTraits& operator=(const TaskTraits& other) = default;
~TaskTraits(); ~TaskTraits();
// Allows tasks with these traits to do file I/O. // Allows tasks with these traits to wait on synchronous file I/O.
TaskTraits& WithFileIO(); TaskTraits& WithFileIO();
// Allows tasks with these traits to wait on things other than file I/O. In
// particular, they may wait on a WaitableEvent or a ConditionVariable, join a
// thread or a process, or make a blocking system call that doesn't involve
// interactions with the file system.
TaskTraits& WithWait();
// Applies |priority| to tasks with these traits. // Applies |priority| to tasks with these traits.
TaskTraits& WithPriority(TaskPriority priority); TaskTraits& WithPriority(TaskPriority priority);
// Applies |shutdown_behavior| to tasks with these traits. // Applies |shutdown_behavior| to tasks with these traits.
TaskTraits& WithShutdownBehavior(TaskShutdownBehavior shutdown_behavior); TaskTraits& WithShutdownBehavior(TaskShutdownBehavior shutdown_behavior);
// Returns true if file I/O is allowed by these traits. // Returns true if waiting on synchronous file I/O is allowed by these traits.
bool with_file_io() const { return with_file_io_; } bool with_file_io() const { return with_file_io_; }
// Returns true if waiting on things other than file I/O is allowed by these
// traits.
bool with_wait() const { return with_wait_; }
// Returns the priority of tasks with these traits. // Returns the priority of tasks with these traits.
TaskPriority priority() const { return priority_; } TaskPriority priority() const { return priority_; }
...@@ -109,6 +119,7 @@ class BASE_EXPORT TaskTraits { ...@@ -109,6 +119,7 @@ class BASE_EXPORT TaskTraits {
private: private:
bool with_file_io_; bool with_file_io_;
bool with_wait_;
TaskPriority priority_; TaskPriority priority_;
TaskShutdownBehavior shutdown_behavior_; TaskShutdownBehavior shutdown_behavior_;
}; };
......
...@@ -808,6 +808,7 @@ bool SequencedWorkerPool::Inner::PostTaskToTaskScheduler( ...@@ -808,6 +808,7 @@ bool SequencedWorkerPool::Inner::PostTaskToTaskScheduler(
static_cast<TaskShutdownBehavior>(sequenced.shutdown_behavior); static_cast<TaskShutdownBehavior>(sequenced.shutdown_behavior);
const TaskTraits traits = TaskTraits() const TaskTraits traits = TaskTraits()
.WithFileIO() .WithFileIO()
.WithWait()
.WithPriority(task_priority_) .WithPriority(task_priority_)
.WithShutdownBehavior(task_shutdown_behavior); .WithShutdownBehavior(task_shutdown_behavior);
return GetTaskSchedulerTaskRunner(sequenced.sequence_token_id, traits) return GetTaskSchedulerTaskRunner(sequenced.sequence_token_id, traits)
...@@ -858,7 +859,7 @@ bool SequencedWorkerPool::Inner::RunsTasksOnCurrentThread() const { ...@@ -858,7 +859,7 @@ bool SequencedWorkerPool::Inner::RunsTasksOnCurrentThread() const {
if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) { if (g_all_pools_state == AllPoolsState::REDIRECTED_TO_TASK_SCHEDULER) {
if (!runs_tasks_on_verifier_) { if (!runs_tasks_on_verifier_) {
runs_tasks_on_verifier_ = CreateTaskRunnerWithTraits( runs_tasks_on_verifier_ = CreateTaskRunnerWithTraits(
TaskTraits().WithFileIO().WithPriority(task_priority_)); TaskTraits().WithFileIO().WithWait().WithPriority(task_priority_));
} }
return runs_tasks_on_verifier_->RunsTasksOnCurrentThread(); return runs_tasks_on_verifier_->RunsTasksOnCurrentThread();
} else { } else {
......
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