xref: /aosp_15_r20/external/angle/src/tests/test_utils/MultiThreadSteps.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // MultiThreadSteps.h:
7 //   Synchronization help for tests that use multiple threads.
8 
9 #include "gl_raii.h"
10 
11 #include <atomic>
12 #include <condition_variable>
13 #include <functional>
14 #include <mutex>
15 #include <thread>
16 
17 class EGLWindow;
18 
19 namespace angle
20 {
21 namespace
22 {
23 // The following class is used by tests that need multiple threads that coordinate their actions
24 // via an enum of "steps".  This enum is the template type E.  The enum must have at least the
25 // following values:
26 //
27 // - Finish   This value indicates that one thread has finished its last step and is cleaning up.
28 //            The other thread waits for this before it does its last step and cleans up.
29 // - Abort    This value indicates that one thread encountered a GL error and has exited.  This
30 //            will cause the other thread (that is waiting for a different step) to also abort.
31 //
32 // This class is RAII.  It is declared at the top of a thread, and will be deconstructed at the end
33 // of the thread's outer block.  If the thread encounters a GL error, the deconstructor will abort
34 // the other thread using the E:Abort step.
35 template <typename E>
36 class ThreadSynchronization
37 {
38   public:
ThreadSynchronization(E * currentStep,std::mutex * mutex,std::condition_variable * condVar)39     ThreadSynchronization(E *currentStep, std::mutex *mutex, std::condition_variable *condVar)
40         : mCurrentStep(currentStep), mMutex(mutex), mCondVar(condVar)
41     {}
~ThreadSynchronization()42     ~ThreadSynchronization()
43     {
44         bool isAborting = false;
45         {
46             // If the other thread isn't finished, cause it to abort.
47             std::unique_lock<std::mutex> lock(*mMutex);
48             isAborting = *mCurrentStep != E::Finish;
49 
50             if (isAborting)
51             {
52                 *mCurrentStep = E::Abort;
53             }
54         }
55         mCondVar->notify_all();
56     }
57 
58     // Helper functions to synchronize the threads so that the operations are executed in the
59     // specific order the test is written for.
waitForStep(E waitStep)60     bool waitForStep(E waitStep)
61     {
62         std::unique_lock<std::mutex> lock(*mMutex);
63         while (*mCurrentStep != waitStep)
64         {
65             // If necessary, abort execution as the other thread has encountered a GL error.
66             if (*mCurrentStep == E::Abort)
67             {
68                 return false;
69             }
70             // Expect increasing order to reduce risk of race conditions / deadlocks.
71             if (*mCurrentStep > waitStep)
72             {
73                 FATAL() << "waitForStep requires increasing order. mCurrentStep="
74                         << (int)*mCurrentStep << ", waitStep=" << (int)waitStep;
75             }
76             mCondVar->wait(lock);
77         }
78 
79         return true;
80     }
81 
nextStep(E newStep)82     void nextStep(E newStep)
83     {
84         {
85             std::unique_lock<std::mutex> lock(*mMutex);
86             *mCurrentStep = newStep;
87         }
88         mCondVar->notify_all();
89     }
90 
91   private:
92     E *mCurrentStep;
93     std::mutex *mMutex;
94     std::condition_variable *mCondVar;
95 };
96 }  // anonymous namespace
97 
98 using LockStepThreadFunc = std::function<void(EGLDisplay, EGLSurface, EGLContext)>;
99 void RunLockStepThreads(EGLWindow *window, size_t threadCount, LockStepThreadFunc threadFuncs[]);
100 void RunLockStepThreadsWithSize(EGLWindow *window,
101                                 EGLint width,
102                                 EGLint height,
103                                 size_t threadCount,
104                                 LockStepThreadFunc threadFuncs[]);
105 }  // namespace angle
106