Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 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 "barrier.h" |
| 18 | |
| 19 | #include <string> |
| 20 | |
| 21 | #include "atomic_integer.h" |
| 22 | #include "common_test.h" |
| 23 | #include "thread_pool.h" |
| 24 | #include "UniquePtr.h" |
| 25 | |
| 26 | namespace art { |
| 27 | class CheckWaitClosure : public Closure { |
| 28 | public: |
| 29 | CheckWaitClosure(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2, |
| 30 | AtomicInteger* count3) |
| 31 | : barrier_(barrier), |
| 32 | count1_(count1), |
| 33 | count2_(count2), |
| 34 | count3_(count3) { |
| 35 | |
| 36 | } |
| 37 | |
| 38 | void Run(Thread* self) { |
| 39 | LOG(INFO) << "Before barrier 1 " << self; |
| 40 | ++*count1_; |
| 41 | barrier_->Wait(self); |
| 42 | ++*count2_; |
| 43 | LOG(INFO) << "Before barrier 2 " << self; |
| 44 | barrier_->Wait(self); |
| 45 | ++*count3_; |
| 46 | LOG(INFO) << "After barrier 2 " << self; |
| 47 | delete this; |
| 48 | } |
| 49 | private: |
| 50 | Barrier* const barrier_; |
| 51 | AtomicInteger* const count1_; |
| 52 | AtomicInteger* const count2_; |
| 53 | AtomicInteger* const count3_; |
| 54 | }; |
| 55 | |
| 56 | class BarrierTest : public CommonTest { |
| 57 | public: |
| 58 | static int32_t num_threads; |
| 59 | }; |
| 60 | |
| 61 | int32_t BarrierTest::num_threads = 4; |
| 62 | |
| 63 | // Check that barrier wait and barrier increment work. |
| 64 | TEST_F(BarrierTest, CheckWait) { |
| 65 | Thread* self = Thread::Current(); |
| 66 | ThreadPool thread_pool(num_threads); |
| 67 | Barrier barrier; |
| 68 | AtomicInteger count1 = 0; |
| 69 | AtomicInteger count2 = 0; |
| 70 | AtomicInteger count3 = 0; |
| 71 | for (int32_t i = 0; i < num_threads; ++i) { |
| 72 | thread_pool.AddTask(self, new CheckWaitClosure(&barrier, &count1, &count2, &count3)); |
| 73 | } |
| 74 | thread_pool.StartWorkers(self); |
| 75 | barrier.Increment(self, num_threads); |
| 76 | // At this point each thread should have passed through the barrier. The first count should be |
| 77 | // equal to num_threads. |
| 78 | EXPECT_EQ(num_threads, count1); |
| 79 | // Count 3 should still be zero since no thread should have gone past the second barrier. |
| 80 | EXPECT_EQ(0, count3); |
| 81 | // Now lets tell the threads to pass again. |
| 82 | barrier.Increment(self, num_threads); |
| 83 | // Count 2 should be equal to num_threads since each thread must have passed the second barrier |
| 84 | // at this point. |
| 85 | EXPECT_EQ(num_threads, count2); |
| 86 | // Wait for all the threads to finish. |
| 87 | thread_pool.Wait(self); |
| 88 | // All three counts should be equal to num_threads now. |
| 89 | EXPECT_EQ(count1, count2); |
| 90 | EXPECT_EQ(count2, count3); |
| 91 | EXPECT_EQ(num_threads, count3); |
| 92 | } |
| 93 | |
| 94 | class CheckPassClosure : public Closure { |
| 95 | public: |
| 96 | CheckPassClosure(Barrier* barrier, AtomicInteger* count, size_t subtasks) |
| 97 | : barrier_(barrier), |
| 98 | count_(count), |
| 99 | subtasks_(subtasks) { |
| 100 | |
| 101 | } |
| 102 | |
| 103 | void Run(Thread* self) { |
| 104 | for (size_t i = 0; i < subtasks_; ++i) { |
| 105 | ++*count_; |
| 106 | // Pass through to next subtask. |
| 107 | barrier_->Pass(self); |
| 108 | } |
| 109 | delete this; |
| 110 | } |
| 111 | private: |
| 112 | Barrier* const barrier_; |
| 113 | AtomicInteger* const count_; |
| 114 | const size_t subtasks_; |
| 115 | }; |
| 116 | |
| 117 | // Check that barrier pass through works. |
| 118 | TEST_F(BarrierTest, CheckPass) { |
| 119 | Thread* self = Thread::Current(); |
| 120 | ThreadPool thread_pool(num_threads); |
| 121 | Barrier barrier; |
| 122 | AtomicInteger count = 0; |
| 123 | const int32_t num_tasks = num_threads * 4; |
| 124 | const int32_t num_sub_tasks = 128; |
| 125 | for (int32_t i = 0; i < num_tasks; ++i) { |
| 126 | thread_pool.AddTask(self, new CheckPassClosure(&barrier, &count, num_sub_tasks)); |
| 127 | } |
| 128 | thread_pool.StartWorkers(self); |
| 129 | const int32_t expected_total_tasks = num_sub_tasks * num_tasks; |
| 130 | // Wait for all the tasks to complete using the barrier. |
| 131 | barrier.Increment(self, expected_total_tasks); |
| 132 | // The total number of completed tasks should be equal to expected_total_tasks. |
| 133 | EXPECT_EQ(count, expected_total_tasks); |
| 134 | } |
| 135 | |
| 136 | } // namespace art |