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 | |
David Sehr | c431b9d | 2018-03-02 12:01:51 -0800 | [diff] [blame] | 21 | #include "base/atomic.h" |
Brian Carlstrom | a1ce1fe | 2014-02-24 23:23:58 -0800 | [diff] [blame] | 22 | #include "common_runtime_test.h" |
Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 23 | #include "mirror/object_array-inl.h" |
Andreas Gampe | b486a98 | 2017-06-01 13:45:54 -0700 | [diff] [blame] | 24 | #include "thread-current-inl.h" |
Andreas Gampe | 8cf9cb3 | 2017-07-19 09:28:38 -0700 | [diff] [blame] | 25 | #include "thread_pool.h" |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 26 | |
| 27 | namespace art { |
Mathieu Chartier | 02b6a78 | 2012-10-26 13:51:26 -0700 | [diff] [blame] | 28 | class CheckWaitTask : public Task { |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 29 | public: |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 30 | CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2) |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 31 | : barrier_(barrier), |
| 32 | count1_(count1), |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 33 | count2_(count2) {} |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 34 | |
Andreas Gampe | fa6a1b0 | 2018-09-07 08:11:55 -0700 | [diff] [blame] | 35 | void Run(Thread* self) override { |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 36 | LOG(INFO) << "Before barrier" << *self; |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 37 | ++*count1_; |
| 38 | barrier_->Wait(self); |
| 39 | ++*count2_; |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 40 | LOG(INFO) << "After barrier" << *self; |
Mathieu Chartier | 02b6a78 | 2012-10-26 13:51:26 -0700 | [diff] [blame] | 41 | } |
| 42 | |
Andreas Gampe | fa6a1b0 | 2018-09-07 08:11:55 -0700 | [diff] [blame] | 43 | void Finalize() override { |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 44 | delete this; |
| 45 | } |
Brian Carlstrom | 0cd7ec2 | 2013-07-17 23:40:20 -0700 | [diff] [blame] | 46 | |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 47 | private: |
| 48 | Barrier* const barrier_; |
| 49 | AtomicInteger* const count1_; |
| 50 | AtomicInteger* const count2_; |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 51 | }; |
| 52 | |
Brian Carlstrom | a1ce1fe | 2014-02-24 23:23:58 -0800 | [diff] [blame] | 53 | class BarrierTest : public CommonRuntimeTest { |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 54 | public: |
| 55 | static int32_t num_threads; |
| 56 | }; |
| 57 | |
| 58 | int32_t BarrierTest::num_threads = 4; |
| 59 | |
| 60 | // Check that barrier wait and barrier increment work. |
| 61 | TEST_F(BarrierTest, CheckWait) { |
| 62 | Thread* self = Thread::Current(); |
Mathieu Chartier | bcd5e9d | 2013-11-13 14:33:28 -0800 | [diff] [blame] | 63 | ThreadPool thread_pool("Barrier test thread pool", num_threads); |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 64 | Barrier barrier(num_threads + 1); // One extra Wait() in main thread. |
| 65 | Barrier timeout_barrier(0); // Only used for sleeping on timeout. |
Brian Carlstrom | 93ba893 | 2013-07-17 21:31:49 -0700 | [diff] [blame] | 66 | AtomicInteger count1(0); |
| 67 | AtomicInteger count2(0); |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 68 | for (int32_t i = 0; i < num_threads; ++i) { |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 69 | thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2)); |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 70 | } |
| 71 | thread_pool.StartWorkers(self); |
Orion Hodson | 88591fe | 2018-03-06 13:35:43 +0000 | [diff] [blame] | 72 | while (count1.load(std::memory_order_relaxed) != num_threads) { |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 73 | timeout_barrier.Increment(self, 1, 100); // sleep 100 msecs |
| 74 | } |
| 75 | // Count 2 should still be zero since no thread should have gone past the barrier. |
Orion Hodson | 88591fe | 2018-03-06 13:35:43 +0000 | [diff] [blame] | 76 | EXPECT_EQ(0, count2.load(std::memory_order_relaxed)); |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 77 | // Perform one additional Wait(), allowing pool threads to proceed. |
| 78 | barrier.Wait(self); |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 79 | // Wait for all the threads to finish. |
Ian Rogers | 1d54e73 | 2013-05-02 21:10:01 -0700 | [diff] [blame] | 80 | thread_pool.Wait(self, true, false); |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 81 | // Both counts should be equal to num_threads now. |
Orion Hodson | 88591fe | 2018-03-06 13:35:43 +0000 | [diff] [blame] | 82 | EXPECT_EQ(count1.load(std::memory_order_relaxed), num_threads); |
| 83 | EXPECT_EQ(count2.load(std::memory_order_relaxed), num_threads); |
Hans Boehm | 5567c11 | 2014-12-02 18:31:31 -0800 | [diff] [blame] | 84 | timeout_barrier.Init(self, 0); // Reset to zero for destruction. |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 85 | } |
| 86 | |
Mathieu Chartier | 02b6a78 | 2012-10-26 13:51:26 -0700 | [diff] [blame] | 87 | class CheckPassTask : public Task { |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 88 | public: |
Mathieu Chartier | 02b6a78 | 2012-10-26 13:51:26 -0700 | [diff] [blame] | 89 | CheckPassTask(Barrier* barrier, AtomicInteger* count, size_t subtasks) |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 90 | : barrier_(barrier), |
| 91 | count_(count), |
Brian Carlstrom | 0cd7ec2 | 2013-07-17 23:40:20 -0700 | [diff] [blame] | 92 | subtasks_(subtasks) {} |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 93 | |
Andreas Gampe | fa6a1b0 | 2018-09-07 08:11:55 -0700 | [diff] [blame] | 94 | void Run(Thread* self) override { |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 95 | for (size_t i = 0; i < subtasks_; ++i) { |
| 96 | ++*count_; |
| 97 | // Pass through to next subtask. |
| 98 | barrier_->Pass(self); |
| 99 | } |
Mathieu Chartier | 02b6a78 | 2012-10-26 13:51:26 -0700 | [diff] [blame] | 100 | } |
| 101 | |
Andreas Gampe | fa6a1b0 | 2018-09-07 08:11:55 -0700 | [diff] [blame] | 102 | void Finalize() override { |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 103 | delete this; |
| 104 | } |
| 105 | private: |
| 106 | Barrier* const barrier_; |
| 107 | AtomicInteger* const count_; |
| 108 | const size_t subtasks_; |
| 109 | }; |
| 110 | |
| 111 | // Check that barrier pass through works. |
| 112 | TEST_F(BarrierTest, CheckPass) { |
| 113 | Thread* self = Thread::Current(); |
Mathieu Chartier | bcd5e9d | 2013-11-13 14:33:28 -0800 | [diff] [blame] | 114 | ThreadPool thread_pool("Barrier test thread pool", num_threads); |
Mathieu Chartier | 35883cc | 2012-11-13 14:08:12 -0800 | [diff] [blame] | 115 | Barrier barrier(0); |
Brian Carlstrom | 93ba893 | 2013-07-17 21:31:49 -0700 | [diff] [blame] | 116 | AtomicInteger count(0); |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 117 | const int32_t num_tasks = num_threads * 4; |
| 118 | const int32_t num_sub_tasks = 128; |
| 119 | for (int32_t i = 0; i < num_tasks; ++i) { |
Mathieu Chartier | 02b6a78 | 2012-10-26 13:51:26 -0700 | [diff] [blame] | 120 | thread_pool.AddTask(self, new CheckPassTask(&barrier, &count, num_sub_tasks)); |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 121 | } |
| 122 | thread_pool.StartWorkers(self); |
| 123 | const int32_t expected_total_tasks = num_sub_tasks * num_tasks; |
| 124 | // Wait for all the tasks to complete using the barrier. |
| 125 | barrier.Increment(self, expected_total_tasks); |
| 126 | // The total number of completed tasks should be equal to expected_total_tasks. |
Orion Hodson | 88591fe | 2018-03-06 13:35:43 +0000 | [diff] [blame] | 127 | EXPECT_EQ(count.load(std::memory_order_relaxed), expected_total_tasks); |
Mathieu Chartier | 0e4627e | 2012-10-23 16:13:36 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | } // namespace art |