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