// Copyright (c) 2012 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/test/sequenced_task_runner_test_template.h" #include #include "base/location.h" namespace base { namespace internal { TaskEvent::TaskEvent(int i, Type type) : i(i), type(type) { } SequencedTaskTracker::SequencedTaskTracker() : next_post_i_(0), task_end_count_(0), task_end_cv_(&lock_) { } void SequencedTaskTracker::PostWrappedNonNestableTask( SequencedTaskRunner* task_runner, OnceClosure task) { AutoLock event_lock(lock_); const int post_i = next_post_i_++; auto wrapped_task = BindOnce(&SequencedTaskTracker::RunTask, this, std::move(task), post_i); task_runner->PostNonNestableTask(FROM_HERE, std::move(wrapped_task)); TaskPosted(post_i); } void SequencedTaskTracker::PostWrappedNestableTask( SequencedTaskRunner* task_runner, OnceClosure task) { AutoLock event_lock(lock_); const int post_i = next_post_i_++; auto wrapped_task = BindOnce(&SequencedTaskTracker::RunTask, this, std::move(task), post_i); task_runner->PostTask(FROM_HERE, std::move(wrapped_task)); TaskPosted(post_i); } void SequencedTaskTracker::PostWrappedDelayedNonNestableTask( SequencedTaskRunner* task_runner, OnceClosure task, TimeDelta delay) { AutoLock event_lock(lock_); const int post_i = next_post_i_++; auto wrapped_task = BindOnce(&SequencedTaskTracker::RunTask, this, std::move(task), post_i); task_runner->PostNonNestableDelayedTask(FROM_HERE, std::move(wrapped_task), delay); TaskPosted(post_i); } void SequencedTaskTracker::PostNonNestableTasks( SequencedTaskRunner* task_runner, int task_count) { for (int i = 0; i < task_count; ++i) { PostWrappedNonNestableTask(task_runner, OnceClosure()); } } void SequencedTaskTracker::RunTask(OnceClosure task, int task_i) { TaskStarted(task_i); if (!task.is_null()) std::move(task).Run(); TaskEnded(task_i); } void SequencedTaskTracker::TaskPosted(int i) { // Caller must own |lock_|. events_.push_back(TaskEvent(i, TaskEvent::POST)); } void SequencedTaskTracker::TaskStarted(int i) { AutoLock lock(lock_); events_.push_back(TaskEvent(i, TaskEvent::START)); } void SequencedTaskTracker::TaskEnded(int i) { AutoLock lock(lock_); events_.push_back(TaskEvent(i, TaskEvent::END)); ++task_end_count_; task_end_cv_.Signal(); } const std::vector& SequencedTaskTracker::GetTaskEvents() const { return events_; } void SequencedTaskTracker::WaitForCompletedTasks(int count) { AutoLock lock(lock_); while (task_end_count_ < count) task_end_cv_.Wait(); } SequencedTaskTracker::~SequencedTaskTracker() = default; void PrintTo(const TaskEvent& event, std::ostream* os) { *os << "(i=" << event.i << ", type="; switch (event.type) { case TaskEvent::POST: *os << "POST"; break; case TaskEvent::START: *os << "START"; break; case TaskEvent::END: *os << "END"; break; } *os << ")"; } namespace { // Returns the task ordinals for the task event type |type| in the order that // they were recorded. std::vector GetEventTypeOrder(const std::vector& events, TaskEvent::Type type) { std::vector tasks; std::vector::const_iterator event; for (event = events.begin(); event != events.end(); ++event) { if (event->type == type) tasks.push_back(event->i); } return tasks; } // Returns all task events for task |task_i|. std::vector GetEventsForTask( const std::vector& events, int task_i) { std::vector task_event_orders; std::vector::const_iterator event; for (event = events.begin(); event != events.end(); ++event) { if (event->i == task_i) task_event_orders.push_back(event->type); } return task_event_orders; } // Checks that the task events for each task in |events| occur in the order // {POST, START, END}, and that there is only one instance of each event type // per task. ::testing::AssertionResult CheckEventOrdersForEachTask( const std::vector& events, int task_count) { std::vector expected_order; expected_order.push_back(TaskEvent::POST); expected_order.push_back(TaskEvent::START); expected_order.push_back(TaskEvent::END); // This is O(n^2), but it runs fast enough currently so is not worth // optimizing. for (int i = 0; i < task_count; ++i) { const std::vector task_events = GetEventsForTask(events, i); if (task_events != expected_order) { return ::testing::AssertionFailure() << "Events for task " << i << " are out of order; expected: " << ::testing::PrintToString(expected_order) << "; actual: " << ::testing::PrintToString(task_events); } } return ::testing::AssertionSuccess(); } // Checks that no two tasks were running at the same time. I.e. the only // events allowed between the START and END of a task are the POSTs of other // tasks. ::testing::AssertionResult CheckNoTaskRunsOverlap( const std::vector& events) { // If > -1, we're currently inside a START, END pair. int current_task_i = -1; std::vector::const_iterator event; for (event = events.begin(); event != events.end(); ++event) { bool spurious_event_found = false; if (current_task_i == -1) { // Not inside a START, END pair. switch (event->type) { case TaskEvent::POST: break; case TaskEvent::START: current_task_i = event->i; break; case TaskEvent::END: spurious_event_found = true; break; } } else { // Inside a START, END pair. bool interleaved_task_detected = false; switch (event->type) { case TaskEvent::POST: if (event->i == current_task_i) spurious_event_found = true; break; case TaskEvent::START: interleaved_task_detected = true; break; case TaskEvent::END: if (event->i != current_task_i) interleaved_task_detected = true; else current_task_i = -1; break; } if (interleaved_task_detected) { return ::testing::AssertionFailure() << "Found event " << ::testing::PrintToString(*event) << " between START and END events for task " << current_task_i << "; event dump: " << ::testing::PrintToString(events); } } if (spurious_event_found) { const int event_i = event - events.begin(); return ::testing::AssertionFailure() << "Spurious event " << ::testing::PrintToString(*event) << " at position " << event_i << "; event dump: " << ::testing::PrintToString(events); } } return ::testing::AssertionSuccess(); } } // namespace ::testing::AssertionResult CheckNonNestableInvariants( const std::vector& events, int task_count) { const std::vector post_order = GetEventTypeOrder(events, TaskEvent::POST); const std::vector start_order = GetEventTypeOrder(events, TaskEvent::START); const std::vector end_order = GetEventTypeOrder(events, TaskEvent::END); if (start_order != post_order) { return ::testing::AssertionFailure() << "Expected START order (which equals actual POST order): \n" << ::testing::PrintToString(post_order) << "\n Actual START order:\n" << ::testing::PrintToString(start_order); } if (end_order != post_order) { return ::testing::AssertionFailure() << "Expected END order (which equals actual POST order): \n" << ::testing::PrintToString(post_order) << "\n Actual END order:\n" << ::testing::PrintToString(end_order); } const ::testing::AssertionResult result = CheckEventOrdersForEachTask(events, task_count); if (!result) return result; return CheckNoTaskRunsOverlap(events); } } // namespace internal // This suite is instantiated in binaries that use //base:test_support. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SequencedTaskRunnerTest); } // namespace base