1*8975f5c5SAndroid Build Coastguard Worker //
2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2023 The ANGLE Project Authors. All rights reserved.
3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker //
6*8975f5c5SAndroid Build Coastguard Worker // Unit tests for ContextMutex.
7*8975f5c5SAndroid Build Coastguard Worker //
8*8975f5c5SAndroid Build Coastguard Worker
9*8975f5c5SAndroid Build Coastguard Worker #include "gtest/gtest.h"
10*8975f5c5SAndroid Build Coastguard Worker
11*8975f5c5SAndroid Build Coastguard Worker #include "libANGLE/ContextMutex.h"
12*8975f5c5SAndroid Build Coastguard Worker
13*8975f5c5SAndroid Build Coastguard Worker namespace
14*8975f5c5SAndroid Build Coastguard Worker {
15*8975f5c5SAndroid Build Coastguard Worker
16*8975f5c5SAndroid Build Coastguard Worker template <class GetContextMutex>
runBasicContextMutexTest(bool expectToPass,GetContextMutex && getContextMutex)17*8975f5c5SAndroid Build Coastguard Worker void runBasicContextMutexTest(bool expectToPass, GetContextMutex &&getContextMutex)
18*8975f5c5SAndroid Build Coastguard Worker {
19*8975f5c5SAndroid Build Coastguard Worker constexpr size_t kThreadCount = 16;
20*8975f5c5SAndroid Build Coastguard Worker constexpr size_t kIterationCount = 50'000;
21*8975f5c5SAndroid Build Coastguard Worker
22*8975f5c5SAndroid Build Coastguard Worker std::array<std::thread, kThreadCount> threads;
23*8975f5c5SAndroid Build Coastguard Worker std::array<egl::ContextMutex *, kThreadCount> contextMutexes = {};
24*8975f5c5SAndroid Build Coastguard Worker
25*8975f5c5SAndroid Build Coastguard Worker std::mutex mutex;
26*8975f5c5SAndroid Build Coastguard Worker std::condition_variable condVar;
27*8975f5c5SAndroid Build Coastguard Worker size_t readyCount = 0;
28*8975f5c5SAndroid Build Coastguard Worker
29*8975f5c5SAndroid Build Coastguard Worker std::atomic<size_t> testVar;
30*8975f5c5SAndroid Build Coastguard Worker
31*8975f5c5SAndroid Build Coastguard Worker for (size_t i = 0; i < kThreadCount; ++i)
32*8975f5c5SAndroid Build Coastguard Worker {
33*8975f5c5SAndroid Build Coastguard Worker threads[i] = std::thread([&, i]() {
34*8975f5c5SAndroid Build Coastguard Worker {
35*8975f5c5SAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock(mutex);
36*8975f5c5SAndroid Build Coastguard Worker contextMutexes[i] = getContextMutex();
37*8975f5c5SAndroid Build Coastguard Worker contextMutexes[i]->addRef();
38*8975f5c5SAndroid Build Coastguard Worker ++readyCount;
39*8975f5c5SAndroid Build Coastguard Worker if (readyCount < kThreadCount)
40*8975f5c5SAndroid Build Coastguard Worker {
41*8975f5c5SAndroid Build Coastguard Worker condVar.wait(lock, [&]() { return readyCount == kThreadCount; });
42*8975f5c5SAndroid Build Coastguard Worker }
43*8975f5c5SAndroid Build Coastguard Worker else
44*8975f5c5SAndroid Build Coastguard Worker {
45*8975f5c5SAndroid Build Coastguard Worker condVar.notify_all();
46*8975f5c5SAndroid Build Coastguard Worker }
47*8975f5c5SAndroid Build Coastguard Worker }
48*8975f5c5SAndroid Build Coastguard Worker for (size_t j = 0; j < kIterationCount; ++j)
49*8975f5c5SAndroid Build Coastguard Worker {
50*8975f5c5SAndroid Build Coastguard Worker egl::ScopedContextMutexLock lock(contextMutexes[i]);
51*8975f5c5SAndroid Build Coastguard Worker const int local = testVar.load(std::memory_order_relaxed);
52*8975f5c5SAndroid Build Coastguard Worker const int newValue = local + 1;
53*8975f5c5SAndroid Build Coastguard Worker testVar.store(newValue, std::memory_order_relaxed);
54*8975f5c5SAndroid Build Coastguard Worker }
55*8975f5c5SAndroid Build Coastguard Worker });
56*8975f5c5SAndroid Build Coastguard Worker }
57*8975f5c5SAndroid Build Coastguard Worker
58*8975f5c5SAndroid Build Coastguard Worker for (size_t i = 0; i < kThreadCount; ++i)
59*8975f5c5SAndroid Build Coastguard Worker {
60*8975f5c5SAndroid Build Coastguard Worker threads[i].join();
61*8975f5c5SAndroid Build Coastguard Worker contextMutexes[i]->release();
62*8975f5c5SAndroid Build Coastguard Worker }
63*8975f5c5SAndroid Build Coastguard Worker
64*8975f5c5SAndroid Build Coastguard Worker if (expectToPass)
65*8975f5c5SAndroid Build Coastguard Worker {
66*8975f5c5SAndroid Build Coastguard Worker EXPECT_EQ(testVar.load(), kThreadCount * kIterationCount);
67*8975f5c5SAndroid Build Coastguard Worker }
68*8975f5c5SAndroid Build Coastguard Worker else
69*8975f5c5SAndroid Build Coastguard Worker {
70*8975f5c5SAndroid Build Coastguard Worker EXPECT_LE(testVar.load(), kThreadCount * kIterationCount);
71*8975f5c5SAndroid Build Coastguard Worker }
72*8975f5c5SAndroid Build Coastguard Worker }
73*8975f5c5SAndroid Build Coastguard Worker
74*8975f5c5SAndroid Build Coastguard Worker // Tests locking of single ContextMutex mutex.
TEST(ContextMutexTest,SingleMutexLock)75*8975f5c5SAndroid Build Coastguard Worker TEST(ContextMutexTest, SingleMutexLock)
76*8975f5c5SAndroid Build Coastguard Worker {
77*8975f5c5SAndroid Build Coastguard Worker egl::ContextMutex *contextMutex = new egl::ContextMutex();
78*8975f5c5SAndroid Build Coastguard Worker contextMutex->addRef();
79*8975f5c5SAndroid Build Coastguard Worker runBasicContextMutexTest(true, [&]() { return contextMutex; });
80*8975f5c5SAndroid Build Coastguard Worker contextMutex->release();
81*8975f5c5SAndroid Build Coastguard Worker }
82*8975f5c5SAndroid Build Coastguard Worker
83*8975f5c5SAndroid Build Coastguard Worker // Tests locking of multiple merged ContextMutex mutexes.
TEST(ContextMutexTest,MultipleMergedMutexLock)84*8975f5c5SAndroid Build Coastguard Worker TEST(ContextMutexTest, MultipleMergedMutexLock)
85*8975f5c5SAndroid Build Coastguard Worker {
86*8975f5c5SAndroid Build Coastguard Worker egl::ContextMutex *contextMutex = new egl::ContextMutex();
87*8975f5c5SAndroid Build Coastguard Worker contextMutex->addRef();
88*8975f5c5SAndroid Build Coastguard Worker runBasicContextMutexTest(true, [&]() {
89*8975f5c5SAndroid Build Coastguard Worker egl::ScopedContextMutexLock lock(contextMutex);
90*8975f5c5SAndroid Build Coastguard Worker egl::ContextMutex *threadMutex = new egl::ContextMutex();
91*8975f5c5SAndroid Build Coastguard Worker egl::ContextMutex::Merge(contextMutex, threadMutex);
92*8975f5c5SAndroid Build Coastguard Worker return threadMutex;
93*8975f5c5SAndroid Build Coastguard Worker });
94*8975f5c5SAndroid Build Coastguard Worker contextMutex->release();
95*8975f5c5SAndroid Build Coastguard Worker }
96*8975f5c5SAndroid Build Coastguard Worker
97*8975f5c5SAndroid Build Coastguard Worker // Tests locking of multiple unmerged ContextMutex mutexes.
TEST(ContextMutexTest,MultipleUnmergedMutexLock)98*8975f5c5SAndroid Build Coastguard Worker TEST(ContextMutexTest, MultipleUnmergedMutexLock)
99*8975f5c5SAndroid Build Coastguard Worker {
100*8975f5c5SAndroid Build Coastguard Worker runBasicContextMutexTest(false, [&]() { return new egl::ContextMutex(); });
101*8975f5c5SAndroid Build Coastguard Worker }
102*8975f5c5SAndroid Build Coastguard Worker
103*8975f5c5SAndroid Build Coastguard Worker // Creates 2N mutexes and 2 threads, then merges N mutex pairs in each thread. Merging order of
104*8975f5c5SAndroid Build Coastguard Worker // the first thread is reversed in the second thread.
TEST(ContextMutexTest,TwoThreadsCrossMerge)105*8975f5c5SAndroid Build Coastguard Worker TEST(ContextMutexTest, TwoThreadsCrossMerge)
106*8975f5c5SAndroid Build Coastguard Worker {
107*8975f5c5SAndroid Build Coastguard Worker constexpr size_t kThreadCount = 2;
108*8975f5c5SAndroid Build Coastguard Worker constexpr size_t kIterationCount = 100;
109*8975f5c5SAndroid Build Coastguard Worker static_assert(kThreadCount % 2 == 0);
110*8975f5c5SAndroid Build Coastguard Worker
111*8975f5c5SAndroid Build Coastguard Worker std::array<std::thread, kThreadCount> threads;
112*8975f5c5SAndroid Build Coastguard Worker std::array<std::array<egl::ContextMutex *, kThreadCount>, kIterationCount> mutexParis = {};
113*8975f5c5SAndroid Build Coastguard Worker
114*8975f5c5SAndroid Build Coastguard Worker // Create mutexes.
115*8975f5c5SAndroid Build Coastguard Worker for (uint32_t i = 0; i < kIterationCount; ++i)
116*8975f5c5SAndroid Build Coastguard Worker {
117*8975f5c5SAndroid Build Coastguard Worker for (uint32_t j = 0; j < kThreadCount; ++j)
118*8975f5c5SAndroid Build Coastguard Worker {
119*8975f5c5SAndroid Build Coastguard Worker mutexParis[i][j] = new egl::ContextMutex();
120*8975f5c5SAndroid Build Coastguard Worker // Call without a lock because no concurrent access is possible.
121*8975f5c5SAndroid Build Coastguard Worker mutexParis[i][j]->addRef();
122*8975f5c5SAndroid Build Coastguard Worker }
123*8975f5c5SAndroid Build Coastguard Worker }
124*8975f5c5SAndroid Build Coastguard Worker
125*8975f5c5SAndroid Build Coastguard Worker std::mutex mutex;
126*8975f5c5SAndroid Build Coastguard Worker std::condition_variable condVar;
127*8975f5c5SAndroid Build Coastguard Worker size_t readyCount = 0;
128*8975f5c5SAndroid Build Coastguard Worker bool flipFlop = false;
129*8975f5c5SAndroid Build Coastguard Worker
130*8975f5c5SAndroid Build Coastguard Worker auto threadJob = [&](size_t lockMutexIndex) {
131*8975f5c5SAndroid Build Coastguard Worker for (size_t i = 0; i < kIterationCount; ++i)
132*8975f5c5SAndroid Build Coastguard Worker {
133*8975f5c5SAndroid Build Coastguard Worker // Lock the first mutex.
134*8975f5c5SAndroid Build Coastguard Worker egl::ScopedContextMutexLock contextMutexLock(mutexParis[i][lockMutexIndex]);
135*8975f5c5SAndroid Build Coastguard Worker // Wait until all threads are ready...
136*8975f5c5SAndroid Build Coastguard Worker {
137*8975f5c5SAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock(mutex);
138*8975f5c5SAndroid Build Coastguard Worker ++readyCount;
139*8975f5c5SAndroid Build Coastguard Worker if (readyCount == kThreadCount)
140*8975f5c5SAndroid Build Coastguard Worker {
141*8975f5c5SAndroid Build Coastguard Worker readyCount = 0;
142*8975f5c5SAndroid Build Coastguard Worker flipFlop = !flipFlop;
143*8975f5c5SAndroid Build Coastguard Worker condVar.notify_all();
144*8975f5c5SAndroid Build Coastguard Worker }
145*8975f5c5SAndroid Build Coastguard Worker else
146*8975f5c5SAndroid Build Coastguard Worker {
147*8975f5c5SAndroid Build Coastguard Worker const size_t prevFlipFlop = flipFlop;
148*8975f5c5SAndroid Build Coastguard Worker condVar.wait(lock, [&]() { return flipFlop != prevFlipFlop; });
149*8975f5c5SAndroid Build Coastguard Worker }
150*8975f5c5SAndroid Build Coastguard Worker }
151*8975f5c5SAndroid Build Coastguard Worker // Merge mutexes.
152*8975f5c5SAndroid Build Coastguard Worker egl::ContextMutex::Merge(mutexParis[i][lockMutexIndex],
153*8975f5c5SAndroid Build Coastguard Worker mutexParis[i][kThreadCount - lockMutexIndex - 1]);
154*8975f5c5SAndroid Build Coastguard Worker }
155*8975f5c5SAndroid Build Coastguard Worker };
156*8975f5c5SAndroid Build Coastguard Worker
157*8975f5c5SAndroid Build Coastguard Worker // Start threads...
158*8975f5c5SAndroid Build Coastguard Worker for (size_t i = 0; i < kThreadCount; ++i)
159*8975f5c5SAndroid Build Coastguard Worker {
160*8975f5c5SAndroid Build Coastguard Worker threads[i] = std::thread([&, i]() { threadJob(i); });
161*8975f5c5SAndroid Build Coastguard Worker }
162*8975f5c5SAndroid Build Coastguard Worker
163*8975f5c5SAndroid Build Coastguard Worker // Join with threads...
164*8975f5c5SAndroid Build Coastguard Worker for (std::thread &thread : threads)
165*8975f5c5SAndroid Build Coastguard Worker {
166*8975f5c5SAndroid Build Coastguard Worker thread.join();
167*8975f5c5SAndroid Build Coastguard Worker }
168*8975f5c5SAndroid Build Coastguard Worker
169*8975f5c5SAndroid Build Coastguard Worker // Destroy mutexes.
170*8975f5c5SAndroid Build Coastguard Worker for (size_t i = 0; i < kIterationCount; ++i)
171*8975f5c5SAndroid Build Coastguard Worker {
172*8975f5c5SAndroid Build Coastguard Worker for (size_t j = 0; j < kThreadCount; ++j)
173*8975f5c5SAndroid Build Coastguard Worker {
174*8975f5c5SAndroid Build Coastguard Worker // Call without a lock because no concurrent access is possible.
175*8975f5c5SAndroid Build Coastguard Worker mutexParis[i][j]->release();
176*8975f5c5SAndroid Build Coastguard Worker }
177*8975f5c5SAndroid Build Coastguard Worker }
178*8975f5c5SAndroid Build Coastguard Worker }
179*8975f5c5SAndroid Build Coastguard Worker
180*8975f5c5SAndroid Build Coastguard Worker } // anonymous namespace
181