|  | /* | 
|  | * Copyright (C) 2012 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "barrier.h" | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/atomic.h" | 
|  | #include "common_runtime_test.h" | 
|  | #include "mirror/object_array-inl.h" | 
|  | #include "thread-current-inl.h" | 
|  | #include "thread_pool.h" | 
|  |  | 
|  | namespace art { | 
|  | class CheckWaitTask : public Task { | 
|  | public: | 
|  | CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2) | 
|  | : barrier_(barrier), | 
|  | count1_(count1), | 
|  | count2_(count2) {} | 
|  |  | 
|  | void Run(Thread* self) override { | 
|  | LOG(INFO) << "Before barrier" << *self; | 
|  | ++*count1_; | 
|  | barrier_->Wait(self); | 
|  | ++*count2_; | 
|  | LOG(INFO) << "After barrier" << *self; | 
|  | } | 
|  |  | 
|  | void Finalize() override { | 
|  | delete this; | 
|  | } | 
|  |  | 
|  | private: | 
|  | Barrier* const barrier_; | 
|  | AtomicInteger* const count1_; | 
|  | AtomicInteger* const count2_; | 
|  | }; | 
|  |  | 
|  | class BarrierTest : public CommonRuntimeTest { | 
|  | public: | 
|  | static int32_t num_threads; | 
|  | }; | 
|  |  | 
|  | int32_t BarrierTest::num_threads = 4; | 
|  |  | 
|  | // Check that barrier wait and barrier increment work. | 
|  | TEST_F(BarrierTest, CheckWait) { | 
|  | Thread* self = Thread::Current(); | 
|  | ThreadPool thread_pool("Barrier test thread pool", num_threads); | 
|  | Barrier barrier(num_threads + 1);  // One extra Wait() in main thread. | 
|  | Barrier timeout_barrier(0);  // Only used for sleeping on timeout. | 
|  | AtomicInteger count1(0); | 
|  | AtomicInteger count2(0); | 
|  | for (int32_t i = 0; i < num_threads; ++i) { | 
|  | thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2)); | 
|  | } | 
|  | thread_pool.StartWorkers(self); | 
|  | while (count1.load(std::memory_order_relaxed) != num_threads) { | 
|  | timeout_barrier.Increment(self, 1, 100);  // sleep 100 msecs | 
|  | } | 
|  | // Count 2 should still be zero since no thread should have gone past the barrier. | 
|  | EXPECT_EQ(0, count2.load(std::memory_order_relaxed)); | 
|  | // Perform one additional Wait(), allowing pool threads to proceed. | 
|  | barrier.Wait(self); | 
|  | // Wait for all the threads to finish. | 
|  | thread_pool.Wait(self, true, false); | 
|  | // Both counts should be equal to num_threads now. | 
|  | EXPECT_EQ(count1.load(std::memory_order_relaxed), num_threads); | 
|  | EXPECT_EQ(count2.load(std::memory_order_relaxed), num_threads); | 
|  | timeout_barrier.Init(self, 0);  // Reset to zero for destruction. | 
|  | } | 
|  |  | 
|  | class CheckPassTask : public Task { | 
|  | public: | 
|  | CheckPassTask(Barrier* barrier, AtomicInteger* count, size_t subtasks) | 
|  | : barrier_(barrier), | 
|  | count_(count), | 
|  | subtasks_(subtasks) {} | 
|  |  | 
|  | void Run(Thread* self) override { | 
|  | for (size_t i = 0; i < subtasks_; ++i) { | 
|  | ++*count_; | 
|  | // Pass through to next subtask. | 
|  | barrier_->Pass(self); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Finalize() override { | 
|  | delete this; | 
|  | } | 
|  | private: | 
|  | Barrier* const barrier_; | 
|  | AtomicInteger* const count_; | 
|  | const size_t subtasks_; | 
|  | }; | 
|  |  | 
|  | // Check that barrier pass through works. | 
|  | TEST_F(BarrierTest, CheckPass) { | 
|  | Thread* self = Thread::Current(); | 
|  | ThreadPool thread_pool("Barrier test thread pool", num_threads); | 
|  | Barrier barrier(0); | 
|  | AtomicInteger count(0); | 
|  | const int32_t num_tasks = num_threads * 4; | 
|  | const int32_t num_sub_tasks = 128; | 
|  | for (int32_t i = 0; i < num_tasks; ++i) { | 
|  | thread_pool.AddTask(self, new CheckPassTask(&barrier, &count, num_sub_tasks)); | 
|  | } | 
|  | thread_pool.StartWorkers(self); | 
|  | const int32_t expected_total_tasks = num_sub_tasks * num_tasks; | 
|  | // Wait for all the tasks to complete using the barrier. | 
|  | barrier.Increment(self, expected_total_tasks); | 
|  | // The total number of completed tasks should be equal to expected_total_tasks. | 
|  | EXPECT_EQ(count.load(std::memory_order_relaxed), expected_total_tasks); | 
|  | } | 
|  |  | 
|  | }  // namespace art |