Line data Source code
1 : // Copyright 2015 the V8 project 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 "src/base/atomicops.h"
6 : #include "src/base/platform/platform.h"
7 : #include "src/cancelable-task.h"
8 : #include "testing/gmock/include/gmock/gmock.h"
9 : #include "testing/gtest/include/gtest/gtest.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : namespace {
15 :
16 : using ResultType = std::atomic<CancelableTaskManager::Id>;
17 :
18 : class CancelableTaskManagerTest;
19 :
20 26 : class TestTask : public Task, public Cancelable {
21 : public:
22 : enum Mode { kDoNothing, kWaitTillCancelTriggered, kCheckNotRun };
23 :
24 : TestTask(CancelableTaskManagerTest* test, ResultType* result, Mode mode);
25 :
26 : // Task override.
27 : void Run() final;
28 :
29 : private:
30 : ResultType* const result_;
31 : const Mode mode_;
32 : CancelableTaskManagerTest* const test_;
33 : };
34 :
35 3 : class SequentialRunner {
36 : public:
37 : explicit SequentialRunner(std::unique_ptr<TestTask> task)
38 3 : : task_(std::move(task)), task_id_(task_->id()) {}
39 :
40 3 : void Run() {
41 3 : task_->Run();
42 : task_.reset();
43 3 : }
44 :
45 : CancelableTaskManager::Id task_id() const { return task_id_; }
46 :
47 : private:
48 : std::unique_ptr<TestTask> task_;
49 : const CancelableTaskManager::Id task_id_;
50 : };
51 :
52 20 : class ThreadedRunner final : public base::Thread {
53 : public:
54 : explicit ThreadedRunner(std::unique_ptr<TestTask> task)
55 : : Thread(Options("runner thread")),
56 : task_(std::move(task)),
57 20 : task_id_(task_->id()) {}
58 :
59 10 : void Run() override {
60 10 : task_->Run();
61 : task_.reset();
62 10 : }
63 :
64 : CancelableTaskManager::Id task_id() const { return task_id_; }
65 :
66 : private:
67 : std::unique_ptr<TestTask> task_;
68 : const CancelableTaskManager::Id task_id_;
69 : };
70 :
71 22 : class CancelableTaskManagerTest : public ::testing::Test {
72 : public:
73 15 : CancelableTaskManager* manager() { return &manager_; }
74 :
75 : std::unique_ptr<TestTask> NewTask(
76 : ResultType* result, TestTask::Mode mode = TestTask::kDoNothing) {
77 13 : return base::make_unique<TestTask>(this, result, mode);
78 : }
79 :
80 : void CancelAndWait() {
81 : cancel_triggered_.store(true);
82 11 : manager_.CancelAndWait();
83 : }
84 :
85 : TryAbortResult TryAbortAll() {
86 : cancel_triggered_.store(true);
87 3 : return manager_.TryAbortAll();
88 : }
89 :
90 : bool cancel_triggered() const { return cancel_triggered_.load(); }
91 :
92 : private:
93 : CancelableTaskManager manager_;
94 : std::atomic<bool> cancel_triggered_{false};
95 : };
96 :
97 : TestTask::TestTask(CancelableTaskManagerTest* test, ResultType* result,
98 : Mode mode)
99 26 : : Cancelable(test->manager()), result_(result), mode_(mode), test_(test) {}
100 :
101 13 : void TestTask::Run() {
102 13 : if (!TryRun()) return;
103 :
104 6 : result_->store(id());
105 :
106 6 : switch (mode_) {
107 : case kWaitTillCancelTriggered:
108 : // Simple busy wait until the main thread tried to cancel.
109 1698810 : while (!test_->cancel_triggered()) {
110 : }
111 : break;
112 : case kCheckNotRun:
113 : // Check that we never execute {RunInternal}.
114 0 : EXPECT_TRUE(false);
115 0 : break;
116 : default:
117 : break;
118 : }
119 : }
120 :
121 : } // namespace
122 :
123 15444 : TEST_F(CancelableTaskManagerTest, EmptyCancelableTaskManager) {
124 : CancelAndWait();
125 1 : }
126 :
127 15444 : TEST_F(CancelableTaskManagerTest, SequentialCancelAndWait) {
128 1 : ResultType result1{0};
129 2 : SequentialRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
130 2 : EXPECT_EQ(0u, result1);
131 : CancelAndWait();
132 2 : EXPECT_EQ(0u, result1);
133 1 : runner1.Run();
134 2 : EXPECT_EQ(0u, result1);
135 1 : }
136 :
137 15444 : TEST_F(CancelableTaskManagerTest, SequentialMultipleTasks) {
138 1 : ResultType result1{0};
139 1 : ResultType result2{0};
140 2 : SequentialRunner runner1(NewTask(&result1));
141 1 : SequentialRunner runner2(NewTask(&result2));
142 2 : EXPECT_EQ(1u, runner1.task_id());
143 2 : EXPECT_EQ(2u, runner2.task_id());
144 :
145 2 : EXPECT_EQ(0u, result1);
146 1 : runner1.Run();
147 2 : EXPECT_EQ(1u, result1);
148 :
149 2 : EXPECT_EQ(0u, result2);
150 1 : runner2.Run();
151 2 : EXPECT_EQ(2u, result2);
152 :
153 : CancelAndWait();
154 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(1));
155 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(2));
156 1 : }
157 :
158 15444 : TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksStarted) {
159 1 : ResultType result1{0};
160 1 : ResultType result2{0};
161 3 : ThreadedRunner runner1(NewTask(&result1, TestTask::kWaitTillCancelTriggered));
162 2 : ThreadedRunner runner2(NewTask(&result2, TestTask::kWaitTillCancelTriggered));
163 1 : runner1.Start();
164 1 : runner2.Start();
165 : // Busy wait on result to make sure both tasks are done.
166 8514878 : while (result1.load() == 0 || result2.load() == 0) {
167 : }
168 : CancelAndWait();
169 1 : runner1.Join();
170 1 : runner2.Join();
171 2 : EXPECT_EQ(1u, result1);
172 2 : EXPECT_EQ(2u, result2);
173 1 : }
174 :
175 15444 : TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksNotRun) {
176 1 : ResultType result1{0};
177 1 : ResultType result2{0};
178 3 : ThreadedRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
179 2 : ThreadedRunner runner2(NewTask(&result2, TestTask::kCheckNotRun));
180 : CancelAndWait();
181 : // Tasks are canceled, hence the runner will bail out and not update result.
182 1 : runner1.Start();
183 1 : runner2.Start();
184 1 : runner1.Join();
185 1 : runner2.Join();
186 2 : EXPECT_EQ(0u, result1);
187 2 : EXPECT_EQ(0u, result2);
188 1 : }
189 :
190 15444 : TEST_F(CancelableTaskManagerTest, RemoveBeforeCancelAndWait) {
191 1 : ResultType result1{0};
192 3 : ThreadedRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
193 1 : CancelableTaskManager::Id id = runner1.task_id();
194 2 : EXPECT_EQ(1u, id);
195 3 : EXPECT_EQ(TryAbortResult::kTaskAborted, manager()->TryAbort(id));
196 1 : runner1.Start();
197 1 : runner1.Join();
198 : CancelAndWait();
199 2 : EXPECT_EQ(0u, result1);
200 1 : }
201 :
202 15444 : TEST_F(CancelableTaskManagerTest, RemoveAfterCancelAndWait) {
203 1 : ResultType result1{0};
204 3 : ThreadedRunner runner1(NewTask(&result1));
205 1 : CancelableTaskManager::Id id = runner1.task_id();
206 2 : EXPECT_EQ(1u, id);
207 1 : runner1.Start();
208 1 : runner1.Join();
209 : CancelAndWait();
210 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(id));
211 2 : EXPECT_EQ(1u, result1);
212 1 : }
213 :
214 15444 : TEST_F(CancelableTaskManagerTest, RemoveUnmanagedId) {
215 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(1));
216 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(2));
217 : CancelAndWait();
218 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(1));
219 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(3));
220 1 : }
221 :
222 15444 : TEST_F(CancelableTaskManagerTest, EmptyTryAbortAll) {
223 2 : EXPECT_EQ(TryAbortResult::kTaskRemoved, TryAbortAll());
224 : CancelAndWait();
225 1 : }
226 :
227 15444 : TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksNotRunTryAbortAll) {
228 1 : ResultType result1{0};
229 1 : ResultType result2{0};
230 3 : ThreadedRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
231 2 : ThreadedRunner runner2(NewTask(&result2, TestTask::kCheckNotRun));
232 2 : EXPECT_EQ(TryAbortResult::kTaskAborted, TryAbortAll());
233 : // Tasks are canceled, hence the runner will bail out and not update result.
234 1 : runner1.Start();
235 1 : runner2.Start();
236 1 : runner1.Join();
237 1 : runner2.Join();
238 2 : EXPECT_EQ(0u, result1);
239 2 : EXPECT_EQ(0u, result2);
240 : CancelAndWait();
241 1 : }
242 :
243 15444 : TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksStartedTryAbortAll) {
244 1 : ResultType result1{0};
245 1 : ResultType result2{0};
246 3 : ThreadedRunner runner1(NewTask(&result1, TestTask::kWaitTillCancelTriggered));
247 2 : ThreadedRunner runner2(NewTask(&result2, TestTask::kWaitTillCancelTriggered));
248 1 : runner1.Start();
249 : // Busy wait on result to make sure task1 is done.
250 6463771 : while (result1.load() == 0) {
251 : }
252 : // If the task saw that we triggered the cancel and finished *before* the
253 : // actual cancel happened, we get {kTaskAborted}. Otherwise, we get
254 : // {kTaskRunning}.
255 2 : EXPECT_THAT(TryAbortAll(),
256 : testing::AnyOf(testing::Eq(TryAbortResult::kTaskAborted),
257 0 : testing::Eq(TryAbortResult::kTaskRunning)));
258 1 : runner2.Start();
259 1 : runner1.Join();
260 1 : runner2.Join();
261 2 : EXPECT_EQ(1u, result1);
262 2 : EXPECT_EQ(0u, result2);
263 : CancelAndWait();
264 1 : }
265 :
266 : } // namespace internal
267 9264 : } // namespace v8
|