2016-04-15 06:23:49 +08:00
|
|
|
// 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/.
|
|
|
|
|
2016-04-15 07:44:10 +08:00
|
|
|
#define EIGEN_USE_THREADS
|
2016-04-15 06:23:49 +08:00
|
|
|
#include "main.h"
|
|
|
|
#include <Eigen/CXX11/ThreadPool>
|
|
|
|
|
2016-04-20 03:57:39 +08:00
|
|
|
// Visual studio doesn't implement a rand_r() function since its
|
|
|
|
// implementation of rand() is already thread safe
|
|
|
|
int rand_reentrant(unsigned int* s) {
|
2016-04-18 10:29:27 +08:00
|
|
|
#ifdef EIGEN_COMP_MSVC_STRICT
|
2016-04-20 06:27:09 +08:00
|
|
|
EIGEN_UNUSED_VARIABLE(s);
|
2016-04-18 10:29:27 +08:00
|
|
|
return rand();
|
2016-04-20 03:57:39 +08:00
|
|
|
#else
|
|
|
|
return rand_r(s);
|
2016-04-18 10:29:27 +08:00
|
|
|
#endif
|
2016-04-20 06:27:09 +08:00
|
|
|
}
|
2016-04-18 10:29:27 +08:00
|
|
|
|
2016-04-15 06:23:49 +08:00
|
|
|
static void test_basic_eventcount()
|
|
|
|
{
|
2016-09-03 10:29:33 +08:00
|
|
|
MaxSizeVector<EventCount::Waiter> waiters(1);
|
2016-09-13 01:31:55 +08:00
|
|
|
waiters.resize(1);
|
2016-04-15 06:23:49 +08:00
|
|
|
EventCount ec(waiters);
|
|
|
|
EventCount::Waiter& w = waiters[0];
|
|
|
|
ec.Notify(false);
|
|
|
|
ec.Prewait(&w);
|
|
|
|
ec.Notify(true);
|
|
|
|
ec.CommitWait(&w);
|
|
|
|
ec.Prewait(&w);
|
|
|
|
ec.CancelWait(&w);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
2016-04-16 02:27:52 +08:00
|
|
|
static const int kEvents = 1 << 16;
|
|
|
|
static const int kQueues = 10;
|
2016-04-15 06:23:49 +08:00
|
|
|
|
2016-09-03 10:29:33 +08:00
|
|
|
MaxSizeVector<EventCount::Waiter> waiters(kThreads);
|
2016-09-13 01:31:55 +08:00
|
|
|
waiters.resize(kThreads);
|
2016-04-15 06:23:49 +08:00
|
|
|
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]() {
|
2016-04-20 03:57:39 +08:00
|
|
|
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;
|
2016-04-15 06:23:49 +08:00
|
|
|
if (queues[idx].Push()) {
|
|
|
|
ec.Notify(false);
|
|
|
|
continue;
|
|
|
|
}
|
2016-04-21 23:47:28 +08:00
|
|
|
EIGEN_THREAD_YIELD();
|
2016-04-20 03:57:39 +08:00
|
|
|
j--;
|
2016-04-15 06:23:49 +08:00
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
2016-04-20 03:57:39 +08:00
|
|
|
unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
2016-04-20 06:27:09 +08:00
|
|
|
for (int j = 0; j < kEvents; j++) {
|
2016-04-20 03:57:39 +08:00
|
|
|
unsigned idx = rand_reentrant(&rnd) % kQueues;
|
2016-04-15 06:23:49 +08:00
|
|
|
if (queues[idx].Pop()) continue;
|
2016-04-20 03:57:39 +08:00
|
|
|
j--;
|
2016-04-15 06:23:49 +08:00
|
|
|
ec.Prewait(&w);
|
|
|
|
bool empty = true;
|
|
|
|
for (int q = 0; q < kQueues; q++) {
|
|
|
|
if (!queues[q].Empty()) {
|
|
|
|
empty = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!empty) {
|
|
|
|
ec.CancelWait(&w);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ec.CommitWait(&w);
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < kThreads; i++) {
|
|
|
|
producers[i]->join();
|
|
|
|
consumers[i]->join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 20:46:15 +08:00
|
|
|
EIGEN_DECLARE_TEST(cxx11_eventcount)
|
2016-04-15 06:23:49 +08:00
|
|
|
{
|
|
|
|
CALL_SUBTEST(test_basic_eventcount());
|
|
|
|
CALL_SUBTEST(test_stress_eventcount());
|
|
|
|
}
|