// Copyright 2015 The Gemmlowp Authors. All Rights Reserved. // // 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 "test.h" #include "../profiling/pthread_everywhere.h" #include <vector> #include "../internal/multi_thread_gemm.h" namespace gemmlowp { class Thread { public: Thread(BlockingCounter* blocking_counter, int number_of_times_to_decrement) : blocking_counter_(blocking_counter), number_of_times_to_decrement_(number_of_times_to_decrement), finished_(false), made_the_last_decrement_(false) { pthread_create(&thread_, nullptr, ThreadFunc, this); } ~Thread() { Join(); } bool Join() const { if (!finished_) { pthread_join(thread_, nullptr); } return made_the_last_decrement_; } private: Thread(const Thread& other) = delete; void ThreadFunc() { for (int i = 0; i < number_of_times_to_decrement_; i++) { Check(!made_the_last_decrement_); made_the_last_decrement_ = blocking_counter_->DecrementCount(); } finished_ = true; } static void* ThreadFunc(void* ptr) { static_cast<Thread*>(ptr)->ThreadFunc(); return nullptr; } BlockingCounter* const blocking_counter_; const int number_of_times_to_decrement_; pthread_t thread_; bool finished_; bool made_the_last_decrement_; }; void test_blocking_counter(BlockingCounter* blocking_counter, int num_threads, int num_decrements_per_thread, int num_decrements_to_wait_for) { std::vector<Thread*> threads; blocking_counter->Reset(num_decrements_to_wait_for); for (int i = 0; i < num_threads; i++) { threads.push_back(new Thread(blocking_counter, num_decrements_per_thread)); } blocking_counter->Wait(); int num_threads_that_made_the_last_decrement = 0; for (int i = 0; i < num_threads; i++) { if (threads[i]->Join()) { num_threads_that_made_the_last_decrement++; } delete threads[i]; } Check(num_threads_that_made_the_last_decrement == 1); } void test_blocking_counter() { BlockingCounter* blocking_counter = new BlockingCounter; // repeating the entire test sequence ensures that we test // non-monotonic changes. for (int repeat = 1; repeat <= 2; repeat++) { for (int num_threads = 1; num_threads <= 16; num_threads++) { for (int num_decrements_per_thread = 1; num_decrements_per_thread <= 64 * 1024; num_decrements_per_thread *= 4) { test_blocking_counter(blocking_counter, num_threads, num_decrements_per_thread, num_threads * num_decrements_per_thread); } } } delete blocking_counter; } } // end namespace gemmlowp int main() { gemmlowp::test_blocking_counter(); }