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/gtest/include/gtest/gtest.h"
9 
10 
11 namespace v8 {
12 namespace internal {
13 
14 namespace {
15 
16 class TestTask : public Task, public Cancelable {
17  public:
18   enum Mode { kDoNothing, kWaitTillCanceledAgain, kCheckNotRun };
19 
TestTask(CancelableTaskManager * parent,base::AtomicWord * result,Mode mode=kDoNothing)20   TestTask(CancelableTaskManager* parent, base::AtomicWord* result,
21            Mode mode = kDoNothing)
22       : Cancelable(parent), result_(result), mode_(mode) {}
23 
24   // Task overrides.
Run()25   void Run() final {
26     if (TryRun()) {
27       RunInternal();
28     }
29   }
30 
31  private:
RunInternal()32   void RunInternal() {
33     base::Release_Store(result_, id());
34 
35     switch (mode_) {
36       case kWaitTillCanceledAgain:
37         // Simple busy wait until the main thread tried to cancel.
38         while (CancelAttempts() == 0) {
39         }
40         break;
41       case kCheckNotRun:
42         // Check that we never execute {RunInternal}.
43         EXPECT_TRUE(false);
44         break;
45       default:
46         break;
47     }
48   }
49 
50   base::AtomicWord* result_;
51   Mode mode_;
52 };
53 
54 
55 class SequentialRunner {
56  public:
SequentialRunner(TestTask * task)57   explicit SequentialRunner(TestTask* task) : task_(task) {}
58 
Run()59   void Run() {
60     task_->Run();
61     delete task_;
62   }
63 
64  private:
65   TestTask* task_;
66 };
67 
68 
69 class ThreadedRunner final : public base::Thread {
70  public:
ThreadedRunner(TestTask * task)71   explicit ThreadedRunner(TestTask* task)
72       : Thread(Options("runner thread")), task_(task) {}
73 
Run()74   virtual void Run() {
75     task_->Run();
76     delete task_;
77   }
78 
79  private:
80   TestTask* task_;
81 };
82 
83 
84 typedef base::AtomicWord ResultType;
85 
86 
GetValue(ResultType * result)87 intptr_t GetValue(ResultType* result) { return base::Acquire_Load(result); }
88 
89 }  // namespace
90 
91 
TEST(CancelableTask,EmptyCancelableTaskManager)92 TEST(CancelableTask, EmptyCancelableTaskManager) {
93   CancelableTaskManager manager;
94   manager.CancelAndWait();
95 }
96 
97 
TEST(CancelableTask,SequentialCancelAndWait)98 TEST(CancelableTask, SequentialCancelAndWait) {
99   CancelableTaskManager manager;
100   ResultType result1 = 0;
101   SequentialRunner runner1(
102       new TestTask(&manager, &result1, TestTask::kCheckNotRun));
103   EXPECT_EQ(GetValue(&result1), 0);
104   manager.CancelAndWait();
105   EXPECT_EQ(GetValue(&result1), 0);
106   runner1.Run();  // Run to avoid leaking the Task.
107   EXPECT_EQ(GetValue(&result1), 0);
108 }
109 
110 
TEST(CancelableTask,SequentialMultipleTasks)111 TEST(CancelableTask, SequentialMultipleTasks) {
112   CancelableTaskManager manager;
113   ResultType result1 = 0;
114   ResultType result2 = 0;
115   TestTask* task1 = new TestTask(&manager, &result1);
116   TestTask* task2 = new TestTask(&manager, &result2);
117   SequentialRunner runner1(task1);
118   SequentialRunner runner2(task2);
119   EXPECT_EQ(task1->id(), 1u);
120   EXPECT_EQ(task2->id(), 2u);
121 
122   EXPECT_EQ(GetValue(&result1), 0);
123   runner1.Run();  // Don't touch task1 after running it.
124   EXPECT_EQ(GetValue(&result1), 1);
125 
126   EXPECT_EQ(GetValue(&result2), 0);
127   runner2.Run();  // Don't touch task2 after running it.
128   EXPECT_EQ(GetValue(&result2), 2);
129 
130   manager.CancelAndWait();
131   EXPECT_FALSE(manager.TryAbort(1));
132   EXPECT_FALSE(manager.TryAbort(2));
133 }
134 
135 
TEST(CancelableTask,ThreadedMultipleTasksStarted)136 TEST(CancelableTask, ThreadedMultipleTasksStarted) {
137   CancelableTaskManager manager;
138   ResultType result1 = 0;
139   ResultType result2 = 0;
140   TestTask* task1 =
141       new TestTask(&manager, &result1, TestTask::kWaitTillCanceledAgain);
142   TestTask* task2 =
143       new TestTask(&manager, &result2, TestTask::kWaitTillCanceledAgain);
144   ThreadedRunner runner1(task1);
145   ThreadedRunner runner2(task2);
146   runner1.Start();
147   runner2.Start();
148   // Busy wait on result to make sure both tasks are done.
149   while ((GetValue(&result1) == 0) || (GetValue(&result2) == 0)) {
150   }
151   manager.CancelAndWait();
152   runner1.Join();
153   runner2.Join();
154   EXPECT_EQ(GetValue(&result1), 1);
155   EXPECT_EQ(GetValue(&result2), 2);
156 }
157 
158 
TEST(CancelableTask,ThreadedMultipleTasksNotRun)159 TEST(CancelableTask, ThreadedMultipleTasksNotRun) {
160   CancelableTaskManager manager;
161   ResultType result1 = 0;
162   ResultType result2 = 0;
163   TestTask* task1 = new TestTask(&manager, &result1, TestTask::kCheckNotRun);
164   TestTask* task2 = new TestTask(&manager, &result2, TestTask::kCheckNotRun);
165   ThreadedRunner runner1(task1);
166   ThreadedRunner runner2(task2);
167   manager.CancelAndWait();
168   // Tasks are canceled, hence the runner will bail out and not update result.
169   runner1.Start();
170   runner2.Start();
171   runner1.Join();
172   runner2.Join();
173   EXPECT_EQ(GetValue(&result1), 0);
174   EXPECT_EQ(GetValue(&result2), 0);
175 }
176 
177 
TEST(CancelableTask,RemoveBeforeCancelAndWait)178 TEST(CancelableTask, RemoveBeforeCancelAndWait) {
179   CancelableTaskManager manager;
180   ResultType result1 = 0;
181   TestTask* task1 = new TestTask(&manager, &result1, TestTask::kCheckNotRun);
182   ThreadedRunner runner1(task1);
183   uint32_t id = task1->id();
184   EXPECT_EQ(id, 1u);
185   EXPECT_TRUE(manager.TryAbort(id));
186   runner1.Start();
187   runner1.Join();
188   manager.CancelAndWait();
189   EXPECT_EQ(GetValue(&result1), 0);
190 }
191 
192 
TEST(CancelableTask,RemoveAfterCancelAndWait)193 TEST(CancelableTask, RemoveAfterCancelAndWait) {
194   CancelableTaskManager manager;
195   ResultType result1 = 0;
196   TestTask* task1 = new TestTask(&manager, &result1);
197   ThreadedRunner runner1(task1);
198   uint32_t id = task1->id();
199   EXPECT_EQ(id, 1u);
200   runner1.Start();
201   runner1.Join();
202   manager.CancelAndWait();
203   EXPECT_FALSE(manager.TryAbort(id));
204   EXPECT_EQ(GetValue(&result1), 1);
205 }
206 
207 
TEST(CancelableTask,RemoveUnmanagedId)208 TEST(CancelableTask, RemoveUnmanagedId) {
209   CancelableTaskManager manager;
210   EXPECT_FALSE(manager.TryAbort(1));
211   EXPECT_FALSE(manager.TryAbort(2));
212   manager.CancelAndWait();
213   EXPECT_FALSE(manager.TryAbort(1));
214   EXPECT_FALSE(manager.TryAbort(3));
215 }
216 
217 }  // namespace internal
218 }  // namespace v8
219