xref: /aosp_15_r20/external/eigen/unsupported/test/cxx11_eventcount.cpp (revision bf2c37156dfe67e5dfebd6d394bad8b2ab5804d4)
1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library
2*bf2c3715SXin Li // for linear algebra.
3*bf2c3715SXin Li //
4*bf2c3715SXin Li // Copyright (C) 2016 Dmitry Vyukov <[email protected]>
5*bf2c3715SXin Li // Copyright (C) 2016 Benoit Steiner <[email protected]>
6*bf2c3715SXin Li //
7*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla
8*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed
9*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10*bf2c3715SXin Li 
11*bf2c3715SXin Li #define EIGEN_USE_THREADS
12*bf2c3715SXin Li #include "main.h"
13*bf2c3715SXin Li #include <Eigen/CXX11/ThreadPool>
14*bf2c3715SXin Li 
15*bf2c3715SXin Li // Visual studio doesn't implement a rand_r() function since its
16*bf2c3715SXin Li // implementation of rand() is already thread safe
rand_reentrant(unsigned int * s)17*bf2c3715SXin Li int rand_reentrant(unsigned int* s) {
18*bf2c3715SXin Li #ifdef EIGEN_COMP_MSVC_STRICT
19*bf2c3715SXin Li   EIGEN_UNUSED_VARIABLE(s);
20*bf2c3715SXin Li   return rand();
21*bf2c3715SXin Li #else
22*bf2c3715SXin Li   return rand_r(s);
23*bf2c3715SXin Li #endif
24*bf2c3715SXin Li }
25*bf2c3715SXin Li 
test_basic_eventcount()26*bf2c3715SXin Li static void test_basic_eventcount()
27*bf2c3715SXin Li {
28*bf2c3715SXin Li   MaxSizeVector<EventCount::Waiter> waiters(1);
29*bf2c3715SXin Li   waiters.resize(1);
30*bf2c3715SXin Li   EventCount ec(waiters);
31*bf2c3715SXin Li   EventCount::Waiter& w = waiters[0];
32*bf2c3715SXin Li   ec.Notify(false);
33*bf2c3715SXin Li   ec.Prewait();
34*bf2c3715SXin Li   ec.Notify(true);
35*bf2c3715SXin Li   ec.CommitWait(&w);
36*bf2c3715SXin Li   ec.Prewait();
37*bf2c3715SXin Li   ec.CancelWait();
38*bf2c3715SXin Li }
39*bf2c3715SXin Li 
40*bf2c3715SXin Li // Fake bounded counter-based queue.
41*bf2c3715SXin Li struct TestQueue {
42*bf2c3715SXin Li   std::atomic<int> val_;
43*bf2c3715SXin Li   static const int kQueueSize = 10;
44*bf2c3715SXin Li 
TestQueueTestQueue45*bf2c3715SXin Li   TestQueue() : val_() {}
46*bf2c3715SXin Li 
~TestQueueTestQueue47*bf2c3715SXin Li   ~TestQueue() { VERIFY_IS_EQUAL(val_.load(), 0); }
48*bf2c3715SXin Li 
PushTestQueue49*bf2c3715SXin Li   bool Push() {
50*bf2c3715SXin Li     int val = val_.load(std::memory_order_relaxed);
51*bf2c3715SXin Li     for (;;) {
52*bf2c3715SXin Li       VERIFY_GE(val, 0);
53*bf2c3715SXin Li       VERIFY_LE(val, kQueueSize);
54*bf2c3715SXin Li       if (val == kQueueSize) return false;
55*bf2c3715SXin Li       if (val_.compare_exchange_weak(val, val + 1, std::memory_order_relaxed))
56*bf2c3715SXin Li         return true;
57*bf2c3715SXin Li     }
58*bf2c3715SXin Li   }
59*bf2c3715SXin Li 
PopTestQueue60*bf2c3715SXin Li   bool Pop() {
61*bf2c3715SXin Li     int val = val_.load(std::memory_order_relaxed);
62*bf2c3715SXin Li     for (;;) {
63*bf2c3715SXin Li       VERIFY_GE(val, 0);
64*bf2c3715SXin Li       VERIFY_LE(val, kQueueSize);
65*bf2c3715SXin Li       if (val == 0) return false;
66*bf2c3715SXin Li       if (val_.compare_exchange_weak(val, val - 1, std::memory_order_relaxed))
67*bf2c3715SXin Li         return true;
68*bf2c3715SXin Li     }
69*bf2c3715SXin Li   }
70*bf2c3715SXin Li 
EmptyTestQueue71*bf2c3715SXin Li   bool Empty() { return val_.load(std::memory_order_relaxed) == 0; }
72*bf2c3715SXin Li };
73*bf2c3715SXin Li 
74*bf2c3715SXin Li const int TestQueue::kQueueSize;
75*bf2c3715SXin Li 
76*bf2c3715SXin Li // A number of producers send messages to a set of consumers using a set of
77*bf2c3715SXin Li // fake queues. Ensure that it does not crash, consumers don't deadlock and
78*bf2c3715SXin Li // number of blocked and unblocked threads match.
test_stress_eventcount()79*bf2c3715SXin Li static void test_stress_eventcount()
80*bf2c3715SXin Li {
81*bf2c3715SXin Li   const int kThreads = std::thread::hardware_concurrency();
82*bf2c3715SXin Li   static const int kEvents = 1 << 16;
83*bf2c3715SXin Li   static const int kQueues = 10;
84*bf2c3715SXin Li 
85*bf2c3715SXin Li   MaxSizeVector<EventCount::Waiter> waiters(kThreads);
86*bf2c3715SXin Li   waiters.resize(kThreads);
87*bf2c3715SXin Li   EventCount ec(waiters);
88*bf2c3715SXin Li   TestQueue queues[kQueues];
89*bf2c3715SXin Li 
90*bf2c3715SXin Li   std::vector<std::unique_ptr<std::thread>> producers;
91*bf2c3715SXin Li   for (int i = 0; i < kThreads; i++) {
92*bf2c3715SXin Li     producers.emplace_back(new std::thread([&ec, &queues]() {
93*bf2c3715SXin Li       unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id()));
94*bf2c3715SXin Li       for (int j = 0; j < kEvents; j++) {
95*bf2c3715SXin Li         unsigned idx = rand_reentrant(&rnd) % kQueues;
96*bf2c3715SXin Li         if (queues[idx].Push()) {
97*bf2c3715SXin Li           ec.Notify(false);
98*bf2c3715SXin Li           continue;
99*bf2c3715SXin Li         }
100*bf2c3715SXin Li         EIGEN_THREAD_YIELD();
101*bf2c3715SXin Li         j--;
102*bf2c3715SXin Li       }
103*bf2c3715SXin Li     }));
104*bf2c3715SXin Li   }
105*bf2c3715SXin Li 
106*bf2c3715SXin Li   std::vector<std::unique_ptr<std::thread>> consumers;
107*bf2c3715SXin Li   for (int i = 0; i < kThreads; i++) {
108*bf2c3715SXin Li     consumers.emplace_back(new std::thread([&ec, &queues, &waiters, i]() {
109*bf2c3715SXin Li       EventCount::Waiter& w = waiters[i];
110*bf2c3715SXin Li       unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id()));
111*bf2c3715SXin Li       for (int j = 0; j < kEvents; j++) {
112*bf2c3715SXin Li         unsigned idx = rand_reentrant(&rnd) % kQueues;
113*bf2c3715SXin Li         if (queues[idx].Pop()) continue;
114*bf2c3715SXin Li         j--;
115*bf2c3715SXin Li         ec.Prewait();
116*bf2c3715SXin Li         bool empty = true;
117*bf2c3715SXin Li         for (int q = 0; q < kQueues; q++) {
118*bf2c3715SXin Li           if (!queues[q].Empty()) {
119*bf2c3715SXin Li             empty = false;
120*bf2c3715SXin Li             break;
121*bf2c3715SXin Li           }
122*bf2c3715SXin Li         }
123*bf2c3715SXin Li         if (!empty) {
124*bf2c3715SXin Li           ec.CancelWait();
125*bf2c3715SXin Li           continue;
126*bf2c3715SXin Li         }
127*bf2c3715SXin Li         ec.CommitWait(&w);
128*bf2c3715SXin Li       }
129*bf2c3715SXin Li     }));
130*bf2c3715SXin Li   }
131*bf2c3715SXin Li 
132*bf2c3715SXin Li   for (int i = 0; i < kThreads; i++) {
133*bf2c3715SXin Li     producers[i]->join();
134*bf2c3715SXin Li     consumers[i]->join();
135*bf2c3715SXin Li   }
136*bf2c3715SXin Li }
137*bf2c3715SXin Li 
EIGEN_DECLARE_TEST(cxx11_eventcount)138*bf2c3715SXin Li EIGEN_DECLARE_TEST(cxx11_eventcount)
139*bf2c3715SXin Li {
140*bf2c3715SXin Li   CALL_SUBTEST(test_basic_eventcount());
141*bf2c3715SXin Li   CALL_SUBTEST(test_stress_eventcount());
142*bf2c3715SXin Li }
143