| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2016 Dmitry Vyukov <dvyukov@google.com> |
| // Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com> |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| #define EIGEN_USE_THREADS |
| #include "main.h" |
| #include <Eigen/ThreadPool> |
| |
| // Visual studio doesn't implement a rand_r() function since its |
| // implementation of rand() is already thread safe |
| int rand_reentrant(unsigned int* s) { |
| #if EIGEN_COMP_MSVC_STRICT |
| EIGEN_UNUSED_VARIABLE(s); |
| return rand(); |
| #else |
| return rand_r(s); |
| #endif |
| } |
| |
| static void test_basic_eventcount() { |
| MaxSizeVector<EventCount::Waiter> waiters(1); |
| waiters.resize(1); |
| EventCount ec(waiters); |
| EventCount::Waiter& w = waiters[0]; |
| ec.Notify(false); |
| ec.Prewait(); |
| ec.Notify(true); |
| ec.CommitWait(&w); |
| ec.Prewait(); |
| ec.CancelWait(); |
| } |
| |
| // Fake bounded counter-based queue. |
| struct TestQueue { |
| std::atomic<int> val_; |
| static const int kQueueSize = 10; |
| |
| TestQueue() : val_() {} |
| |
| ~TestQueue() { VERIFY_IS_EQUAL(val_.load(), 0); } |
| |
| bool Push() { |
| int val = val_.load(std::memory_order_relaxed); |
| for (;;) { |
| VERIFY_GE(val, 0); |
| VERIFY_LE(val, kQueueSize); |
| if (val == kQueueSize) return false; |
| if (val_.compare_exchange_weak(val, val + 1, std::memory_order_relaxed)) return true; |
| } |
| } |
| |
| bool Pop() { |
| int val = val_.load(std::memory_order_relaxed); |
| for (;;) { |
| VERIFY_GE(val, 0); |
| VERIFY_LE(val, kQueueSize); |
| if (val == 0) return false; |
| if (val_.compare_exchange_weak(val, val - 1, std::memory_order_relaxed)) return true; |
| } |
| } |
| |
| bool Empty() { return val_.load(std::memory_order_relaxed) == 0; } |
| }; |
| |
| const int TestQueue::kQueueSize; |
| |
| // A number of producers send messages to a set of consumers using a set of |
| // fake queues. Ensure that it does not crash, consumers don't deadlock and |
| // number of blocked and unblocked threads match. |
| static void test_stress_eventcount() { |
| const int kThreads = std::thread::hardware_concurrency(); |
| static const int kEvents = 1 << 16; |
| static const int kQueues = 10; |
| |
| MaxSizeVector<EventCount::Waiter> waiters(kThreads); |
| waiters.resize(kThreads); |
| EventCount ec(waiters); |
| TestQueue queues[kQueues]; |
| |
| std::vector<std::unique_ptr<std::thread>> producers; |
| for (int i = 0; i < kThreads; i++) { |
| producers.emplace_back(new std::thread([&ec, &queues]() { |
| unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id())); |
| for (int j = 0; j < kEvents; j++) { |
| unsigned idx = rand_reentrant(&rnd) % kQueues; |
| if (queues[idx].Push()) { |
| ec.Notify(false); |
| continue; |
| } |
| EIGEN_THREAD_YIELD(); |
| j--; |
| } |
| })); |
| } |
| |
| std::vector<std::unique_ptr<std::thread>> consumers; |
| for (int i = 0; i < kThreads; i++) { |
| consumers.emplace_back(new std::thread([&ec, &queues, &waiters, i]() { |
| EventCount::Waiter& w = waiters[i]; |
| unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id())); |
| for (int j = 0; j < kEvents; j++) { |
| unsigned idx = rand_reentrant(&rnd) % kQueues; |
| if (queues[idx].Pop()) continue; |
| j--; |
| ec.Prewait(); |
| bool empty = true; |
| for (int q = 0; q < kQueues; q++) { |
| if (!queues[q].Empty()) { |
| empty = false; |
| break; |
| } |
| } |
| if (!empty) { |
| ec.CancelWait(); |
| continue; |
| } |
| ec.CommitWait(&w); |
| } |
| })); |
| } |
| |
| for (int i = 0; i < kThreads; i++) { |
| producers[i]->join(); |
| consumers[i]->join(); |
| } |
| } |
| |
| EIGEN_DECLARE_TEST(cxx11_eventcount) { |
| CALL_SUBTEST(test_basic_eventcount()); |
| CALL_SUBTEST(test_stress_eventcount()); |
| } |