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   thread_pool.Wait(self, false, false);
99 }
100 
101 class TreeTask : public Task {
102  public:
TreeTask(ThreadPool * const thread_pool,AtomicInteger * count,int depth)103   TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth)
104       : thread_pool_(thread_pool),
105         count_(count),
106         depth_(depth) {}
107 
Run(Thread * self)108   void Run(Thread* self) {
109     if (depth_ > 1) {
110       thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1));
111       thread_pool_->AddTask(self, new TreeTask(thread_pool_, count_, depth_ - 1));
112     }
113     // Increment the counter which keeps track of work completed.
114     ++*count_;
115   }
116 
Finalize()117   void Finalize() {
118     delete this;
119   }
120 
121  private:
122   ThreadPool* const thread_pool_;
123   AtomicInteger* const count_;
124   const int depth_;
125 };
126 
127 // Test that adding new tasks from within a task works.
TEST_F(ThreadPoolTest,RecursiveTest)128 TEST_F(ThreadPoolTest, RecursiveTest) {
129   Thread* self = Thread::Current();
130   ThreadPool thread_pool("Thread pool test thread pool", num_threads);
131   AtomicInteger count(0);
132   static const int depth = 8;
133   thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth));
134   thread_pool.StartWorkers(self);
135   thread_pool.Wait(self, true, false);
136   EXPECT_EQ((1 << depth) - 1, count.LoadSequentiallyConsistent());
137 }
138 
139 }  // namespace art
140