|  | // 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()); | 
|  | } |