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
25namespace art {
26
27class CountTask : public Task {
28 public:
29  explicit CountTask(AtomicInteger* count) : count_(count), verbose_(false) {}
30
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
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
53class ThreadPoolTest : public CommonRuntimeTest {
54 public:
55  static int32_t num_threads;
56};
57
58int32_t ThreadPoolTest::num_threads = 4;
59
60// Check that the thread pool actually runs tasks that you assign it.
61TEST_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
76TEST_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
105class TreeTask : public Task {
106 public:
107  TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth)
108      : thread_pool_(thread_pool),
109        count_(count),
110        depth_(depth) {}
111
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
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.
132TEST_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