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 20 TestTask(CancelableTaskManager* parent, base::AtomicWord* result, 21 Mode mode = kDoNothing) 22 : Cancelable(parent), result_(result), mode_(mode) {} 23 24 // Task overrides. 25 void Run() final { 26 if (TryRun()) { 27 RunInternal(); 28 } 29 } 30 31 private: 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: 57 explicit SequentialRunner(TestTask* task) : task_(task) {} 58 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: 71 explicit ThreadedRunner(TestTask* task) 72 : Thread(Options("runner thread")), task_(task) {} 73 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 87 intptr_t GetValue(ResultType* result) { return base::Acquire_Load(result); } 88 89 } // namespace 90 91 92 TEST(CancelableTask, EmptyCancelableTaskManager) { 93 CancelableTaskManager manager; 94 manager.CancelAndWait(); 95 } 96 97 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 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 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 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 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 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 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