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 "thread-inl.h"
24 
25 namespace art {
26 
27 class CountTask : public Task {
28  public:
CountTask(AtomicInteger * count)29   explicit CountTask(AtomicInteger* count) : count_(count), verbose_(false) {}
30 
Run(Thread * self)31   void Run(Thread* self) {
32     if (verbose_) {
33       LOG(INFO) << "Running: " << *self;
34     }
35     // Simulate doing some work.
36     usleep(100);
37     // Increment the counter which keeps track of work completed.
38     ++*count_;
39   }
40 
Finalize()41   void Finalize() {
42     if (verbose_) {
43       LOG(INFO) << "Finalizing: " << *Thread::Current();
44     }
45     delete this;
46   }
47 
48  private:
49   AtomicInteger* const count_;
50   const bool verbose_;
51 };
52 
53 class ThreadPoolTest : public CommonRuntimeTest {
54  public:
55   static int32_t num_threads;
56 };
57 
58 int32_t ThreadPoolTest::num_threads = 4;
59 
60 // Check that the thread pool actually runs tasks that you assign it.
TEST_F(ThreadPoolTest,CheckRun)61 TEST_F(ThreadPoolTest, CheckRun) {
62   Thread* self = Thread::Current();
63   ThreadPool thread_pool("Thread pool test thread pool", num_threads);
64   AtomicInteger count(0);
65   static const int32_t num_tasks = num_threads * 4;
66   for (int32_t i = 0; i < num_tasks; ++i) {
67     thread_pool.AddTask(self, new CountTask(&count));
68   }
69   thread_pool.StartWorkers(self);
70   // Wait for tasks to complete.
71   thread_pool.Wait(self, true, false);
72   // Make sure that we finished all the work.
73   EXPECT_EQ(num_tasks, count.LoadSequentiallyConsistent());
74 }
75 
TEST_F(ThreadPoolTest,StopStart)76 TEST_F(ThreadPoolTest, StopStart) {
77   Thread* self = Thread::Current();
78   ThreadPool thread_pool("Thread pool test thread pool", num_threads);
79   AtomicInteger count(0);
80   static const int32_t num_tasks = num_threads * 4;
81   for (int32_t i = 0; i < num_tasks; ++i) {
82     thread_pool.AddTask(self, new CountTask(&count));
83   }
84   usleep(200);
85   // Check that no threads started prematurely.
86   EXPECT_EQ(0, count.LoadSequentiallyConsistent());
87   // Signal the threads to start processing tasks.
88   thread_pool.StartWorkers(self);
89   usleep(200);
90   thread_pool.StopWorkers(self);
91   AtomicInteger bad_count(0);
92   thread_pool.AddTask(self, new CountTask(&bad_count));
93   usleep(200);
94   // Ensure that the task added after the workers were stopped doesn't get run.
95   EXPECT_EQ(0, bad_count.LoadSequentiallyConsistent());
96   // Allow tasks to finish up and delete themselves.
97   thread_pool.StartWorkers(self);
98   while (count.LoadSequentiallyConsistent() != num_tasks &&
99       bad_count.LoadSequentiallyConsistent() != 1) {
100     usleep(200);
101   }
102   thread_pool.StopWorkers(self);
103 }
104 
105 class TreeTask : public Task {
106  public:
TreeTask(ThreadPool * const thread_pool,AtomicInteger * count,int depth)107   TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth)
108       : thread_pool_(thread_pool),
109         count_(count),
110         depth_(depth) {}
111 
Run(Thread * self)112   void Run(Thread* self) {
113     if (depth_ > 1) {
114       thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1));
115       thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1));
116     }
117     // Increment the counter which keeps track of work completed.
118     ++*count_;
119   }
120 
Finalize()121   void Finalize() {
122     delete this;
123   }
124 
125  private:
126   ThreadPool* const thread_pool_;
127   AtomicInteger* const count_;
128   const int depth_;
129 };
130 
131 // Test that adding new tasks from within a task works.
TEST_F(ThreadPoolTest,RecursiveTest)132 TEST_F(ThreadPoolTest, RecursiveTest) {
133   Thread* self = Thread::Current();
134   ThreadPool thread_pool("Thread pool test thread pool", num_threads);
135   AtomicInteger count(0);
136   static const int depth = 8;
137   thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth));
138   thread_pool.StartWorkers(self);
139   thread_pool.Wait(self, true, false);
140   EXPECT_EQ((1 << depth) - 1, count.LoadSequentiallyConsistent());
141 }
142 
143 }  // namespace art
144