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