xref: /aosp_15_r20/external/angle/src/libANGLE/ContextMutex_unittest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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