ajwong | 8d51d07 | 2017-02-10 08:52:40 +0900 | [diff] [blame] | 1 | // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "base/pending_task.h" |
| 6 | |
| 7 | #include <vector> |
| 8 | |
| 9 | #include "base/bind.h" |
| 10 | #include "base/message_loop/message_loop.h" |
| 11 | #include "base/run_loop.h" |
| 12 | #include "base/strings/stringprintf.h" |
| 13 | #include "base/threading/thread.h" |
| 14 | #include "testing/gtest/include/gtest/gtest.h" |
| 15 | |
| 16 | namespace base { |
| 17 | |
| 18 | class PendingTaskTest : public ::testing::Test { |
| 19 | public: |
| 20 | PendingTaskTest() = default; |
| 21 | |
| 22 | ~PendingTaskTest() override = default; |
| 23 | |
| 24 | protected: |
| 25 | using ExpectedTrace = std::vector<const void*>; |
| 26 | |
| 27 | static void VerifyTraceAndPost( |
| 28 | const scoped_refptr<TaskRunner>& task_runner, |
| 29 | const tracked_objects::Location& posted_from, |
| 30 | const tracked_objects::Location& next_from_here, |
| 31 | const std::vector<const void*>& expected_trace, |
| 32 | Closure task) { |
| 33 | SCOPED_TRACE(StringPrintf("Callback Depth: %zu", expected_trace.size())); |
| 34 | |
| 35 | // Beyond depth + 1, the trace is nonsensical because there haven't been |
| 36 | // enough nested tasks called. |
| 37 | const PendingTask* current_pending_task = |
| 38 | MessageLoop::current()->current_pending_task_; |
| 39 | size_t window = std::min(current_pending_task->task_backtrace.size(), |
| 40 | expected_trace.size()); |
| 41 | |
| 42 | EXPECT_EQ(posted_from, |
| 43 | MessageLoop::current()->current_pending_task_->posted_from); |
| 44 | for (size_t i = 0; i < window; i++) { |
| 45 | SCOPED_TRACE(StringPrintf("Trace frame: %zu", i)); |
| 46 | EXPECT_EQ(expected_trace[i], current_pending_task->task_backtrace[i]); |
| 47 | } |
| 48 | task_runner->PostTask(next_from_here, std::move(task)); |
| 49 | } |
| 50 | |
| 51 | static void RunTwo(Closure c1, Closure c2) { |
| 52 | c1.Run(); |
| 53 | c2.Run(); |
| 54 | } |
| 55 | }; |
| 56 | |
| 57 | // Ensure the task backtrace populates correctly. |
| 58 | TEST_F(PendingTaskTest, SingleThreadedSimple) { |
| 59 | MessageLoop loop; |
| 60 | const tracked_objects::Location& location0 = FROM_HERE; |
| 61 | const tracked_objects::Location& location1 = FROM_HERE; |
| 62 | const tracked_objects::Location& location2 = FROM_HERE; |
| 63 | const tracked_objects::Location& location3 = FROM_HERE; |
| 64 | const tracked_objects::Location& location4 = FROM_HERE; |
| 65 | const tracked_objects::Location& location5 = FROM_HERE; |
| 66 | |
| 67 | Closure task5 = Bind( |
| 68 | &PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location4, |
| 69 | location5, |
| 70 | ExpectedTrace({location3.program_counter(), location2.program_counter(), |
| 71 | location1.program_counter(), location0.program_counter()}), |
| 72 | Bind(&DoNothing)); |
| 73 | Closure task4 = Bind( |
| 74 | &PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location3, |
| 75 | location4, |
| 76 | ExpectedTrace({location2.program_counter(), location1.program_counter(), |
| 77 | location0.program_counter(), nullptr}), |
| 78 | task5); |
| 79 | Closure task3 = Bind( |
| 80 | &PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location2, |
| 81 | location3, ExpectedTrace({location1.program_counter(), |
| 82 | location0.program_counter(), nullptr, nullptr}), |
| 83 | task4); |
| 84 | Closure task2 = |
| 85 | Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location1, |
| 86 | location2, ExpectedTrace({location0.program_counter()}), task3); |
| 87 | Closure task1 = Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), |
| 88 | location0, location1, ExpectedTrace({}), task2); |
| 89 | |
| 90 | loop.task_runner()->PostTask(location0, task1); |
| 91 | |
| 92 | RunLoop().RunUntilIdle(); |
| 93 | } |
| 94 | |
| 95 | // Post a task onto another thread. Ensure on the other thread, it has the |
| 96 | // right stack trace. |
| 97 | TEST_F(PendingTaskTest, MultipleThreads) { |
| 98 | MessageLoop loop; // Implicitly "thread a." |
| 99 | Thread thread_b("pt_test_b"); |
| 100 | Thread thread_c("pt_test_c"); |
| 101 | thread_b.StartAndWaitForTesting(); |
| 102 | thread_c.StartAndWaitForTesting(); |
| 103 | |
| 104 | const tracked_objects::Location& location_a0 = FROM_HERE; |
| 105 | const tracked_objects::Location& location_a1 = FROM_HERE; |
| 106 | const tracked_objects::Location& location_a2 = FROM_HERE; |
| 107 | const tracked_objects::Location& location_a3 = FROM_HERE; |
| 108 | |
| 109 | const tracked_objects::Location& location_b0 = FROM_HERE; |
| 110 | const tracked_objects::Location& location_b1 = FROM_HERE; |
| 111 | |
| 112 | const tracked_objects::Location& location_c0 = FROM_HERE; |
| 113 | |
| 114 | // On thread c, post a task back to thread a that verifies its trace |
| 115 | // and terminates after one more self-post. |
| 116 | Closure task_a2 = |
| 117 | Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), |
| 118 | location_a2, location_a3, |
| 119 | ExpectedTrace( |
| 120 | {location_c0.program_counter(), location_b0.program_counter(), |
| 121 | location_a1.program_counter(), location_a0.program_counter()}), |
| 122 | Bind(&DoNothing)); |
| 123 | Closure task_c0 = Bind(&PendingTaskTest::VerifyTraceAndPost, |
| 124 | loop.task_runner(), location_c0, location_a2, |
| 125 | ExpectedTrace({location_b0.program_counter(), |
| 126 | location_a1.program_counter(), |
| 127 | location_a0.program_counter()}), |
| 128 | task_a2); |
| 129 | |
| 130 | // On thread b run two tasks that conceptually come from the same location |
| 131 | // (managed via RunTwo().) One will post back to thread b and another will |
| 132 | // post to thread c to test spawning multiple tasks on different message |
| 133 | // loops. The task posted to thread c will not get location b1 whereas the |
| 134 | // one posted back to thread b will. |
| 135 | Closure task_b0_fork = |
| 136 | Bind(&PendingTaskTest::VerifyTraceAndPost, |
| 137 | thread_c.message_loop()->task_runner(), location_b0, location_c0, |
| 138 | ExpectedTrace({location_a1.program_counter(), |
| 139 | location_a0.program_counter(), nullptr}), |
| 140 | task_c0); |
| 141 | Closure task_b0_local = |
| 142 | Bind(&PendingTaskTest::VerifyTraceAndPost, |
| 143 | thread_b.message_loop()->task_runner(), location_b0, location_b1, |
| 144 | ExpectedTrace({location_a1.program_counter(), |
| 145 | location_a0.program_counter(), nullptr}), |
| 146 | Bind(&DoNothing)); |
| 147 | |
| 148 | // Push one frame onto the stack in thread a then pass to thread b. |
| 149 | Closure task_a1 = |
| 150 | Bind(&PendingTaskTest::VerifyTraceAndPost, |
| 151 | thread_b.message_loop()->task_runner(), location_a1, location_b0, |
| 152 | ExpectedTrace({location_a0.program_counter(), nullptr}), |
| 153 | Bind(&PendingTaskTest::RunTwo, task_b0_local, task_b0_fork)); |
| 154 | Closure task_a0 = |
| 155 | Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), |
| 156 | location_a0, location_a1, ExpectedTrace({nullptr}), task_a1); |
| 157 | |
| 158 | loop.task_runner()->PostTask(location_a0, task_a0); |
| 159 | |
| 160 | RunLoop().RunUntilIdle(); |
| 161 | |
| 162 | thread_b.FlushForTesting(); |
| 163 | thread_b.Stop(); |
| 164 | |
| 165 | thread_c.FlushForTesting(); |
| 166 | thread_c.Stop(); |
| 167 | } |
| 168 | |
| 169 | } // namespace base |