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