1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "thread_pool.h" 18 19 #include <string> 20 21 #include "atomic.h" 22 #include "common_runtime_test.h" 23 #include "scoped_thread_state_change-inl.h" 24 #include "thread-inl.h" 25 26 namespace art { 27 28 class CountTask : public Task { 29 public: 30 explicit CountTask(AtomicInteger* count) : count_(count), verbose_(false) {} 31 32 void Run(Thread* self) { 33 if (verbose_) { 34 LOG(INFO) << "Running: " << *self; 35 } 36 // Simulate doing some work. 37 usleep(100); 38 // Increment the counter which keeps track of work completed. 39 ++*count_; 40 } 41 42 void Finalize() { 43 if (verbose_) { 44 LOG(INFO) << "Finalizing: " << *Thread::Current(); 45 } 46 delete this; 47 } 48 49 private: 50 AtomicInteger* const count_; 51 const bool verbose_; 52 }; 53 54 class ThreadPoolTest : public CommonRuntimeTest { 55 public: 56 static int32_t num_threads; 57 }; 58 59 int32_t ThreadPoolTest::num_threads = 4; 60 61 // Check that the thread pool actually runs tasks that you assign it. 62 TEST_F(ThreadPoolTest, CheckRun) { 63 Thread* self = Thread::Current(); 64 ThreadPool thread_pool("Thread pool test thread pool", num_threads); 65 AtomicInteger count(0); 66 static const int32_t num_tasks = num_threads * 4; 67 for (int32_t i = 0; i < num_tasks; ++i) { 68 thread_pool.AddTask(self, new CountTask(&count)); 69 } 70 thread_pool.StartWorkers(self); 71 // Wait for tasks to complete. 72 thread_pool.Wait(self, true, false); 73 // Make sure that we finished all the work. 74 EXPECT_EQ(num_tasks, count.LoadSequentiallyConsistent()); 75 } 76 77 TEST_F(ThreadPoolTest, StopStart) { 78 Thread* self = Thread::Current(); 79 ThreadPool thread_pool("Thread pool test thread pool", num_threads); 80 AtomicInteger count(0); 81 static const int32_t num_tasks = num_threads * 4; 82 for (int32_t i = 0; i < num_tasks; ++i) { 83 thread_pool.AddTask(self, new CountTask(&count)); 84 } 85 usleep(200); 86 // Check that no threads started prematurely. 87 EXPECT_EQ(0, count.LoadSequentiallyConsistent()); 88 // Signal the threads to start processing tasks. 89 thread_pool.StartWorkers(self); 90 usleep(200); 91 thread_pool.StopWorkers(self); 92 AtomicInteger bad_count(0); 93 thread_pool.AddTask(self, new CountTask(&bad_count)); 94 usleep(200); 95 // Ensure that the task added after the workers were stopped doesn't get run. 96 EXPECT_EQ(0, bad_count.LoadSequentiallyConsistent()); 97 // Allow tasks to finish up and delete themselves. 98 thread_pool.StartWorkers(self); 99 thread_pool.Wait(self, false, false); 100 } 101 102 TEST_F(ThreadPoolTest, StopWait) { 103 Thread* self = Thread::Current(); 104 ThreadPool thread_pool("Thread pool test thread pool", num_threads); 105 106 AtomicInteger count(0); 107 static const int32_t num_tasks = num_threads * 100; 108 for (int32_t i = 0; i < num_tasks; ++i) { 109 thread_pool.AddTask(self, new CountTask(&count)); 110 } 111 112 // Signal the threads to start processing tasks. 113 thread_pool.StartWorkers(self); 114 usleep(200); 115 thread_pool.StopWorkers(self); 116 117 thread_pool.Wait(self, false, false); // We should not deadlock here. 118 119 // Drain the task list. Note: we have to restart here, as no tasks will be finished when 120 // the pool is stopped. 121 thread_pool.StartWorkers(self); 122 thread_pool.Wait(self, /* do_work */ true, false); 123 } 124 125 class TreeTask : public Task { 126 public: 127 TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth) 128 : thread_pool_(thread_pool), 129 count_(count), 130 depth_(depth) {} 131 132 void Run(Thread* self) { 133 if (depth_ > 1) { 134 thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1)); 135 thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1)); 136 } 137 // Increment the counter which keeps track of work completed. 138 ++*count_; 139 } 140 141 void Finalize() { 142 delete this; 143 } 144 145 private: 146 ThreadPool* const thread_pool_; 147 AtomicInteger* const count_; 148 const int depth_; 149 }; 150 151 // Test that adding new tasks from within a task works. 152 TEST_F(ThreadPoolTest, RecursiveTest) { 153 Thread* self = Thread::Current(); 154 ThreadPool thread_pool("Thread pool test thread pool", num_threads); 155 AtomicInteger count(0); 156 static const int depth = 8; 157 thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth)); 158 thread_pool.StartWorkers(self); 159 thread_pool.Wait(self, true, false); 160 EXPECT_EQ((1 << depth) - 1, count.LoadSequentiallyConsistent()); 161 } 162 163 class PeerTask : public Task { 164 public: 165 PeerTask() {} 166 167 void Run(Thread* self) { 168 ScopedObjectAccess soa(self); 169 CHECK(self->GetPeer() != nullptr); 170 } 171 172 void Finalize() { 173 delete this; 174 } 175 }; 176 177 class NoPeerTask : public Task { 178 public: 179 NoPeerTask() {} 180 181 void Run(Thread* self) { 182 ScopedObjectAccess soa(self); 183 CHECK(self->GetPeer() == nullptr); 184 } 185 186 void Finalize() { 187 delete this; 188 } 189 }; 190 191 // Tests for create_peer functionality. 192 TEST_F(ThreadPoolTest, PeerTest) { 193 Thread* self = Thread::Current(); 194 { 195 ThreadPool thread_pool("Thread pool test thread pool", 1); 196 thread_pool.AddTask(self, new NoPeerTask()); 197 thread_pool.StartWorkers(self); 198 thread_pool.Wait(self, false, false); 199 } 200 201 { 202 // To create peers, the runtime needs to be started. 203 self->TransitionFromSuspendedToRunnable(); 204 bool started = runtime_->Start(); 205 ASSERT_TRUE(started); 206 207 ThreadPool thread_pool("Thread pool test thread pool", 1, true); 208 thread_pool.AddTask(self, new PeerTask()); 209 thread_pool.StartWorkers(self); 210 thread_pool.Wait(self, false, false); 211 } 212 } 213 214 } // namespace art 215