src/third_party/angle/src/tests/egl_tests/EGLContextASANTest.cpp
// Tests that creating resources works after freeing the share context.
Tests that creating resources works after freeing the share context.
Tests that creating resources works after freeing the share context .
TEST_P(EGLContextASANTest, DestroyContextInUse)
{
    ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
    EGLDisplay display = getEGLWindow()->getDisplay();
    EGLConfig config   = getEGLWindow()->getConfig();
    EGLSurface surface = getEGLWindow()->getSurface();
    const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,
                                     getEGLWindow()->getClientMajorVersion(), EGL_NONE};
    EGLContext context = eglCreateContext(display, config, nullptr, contextAttribs);
    ASSERT_EGL_SUCCESS();
    ASSERT_TRUE(context != EGL_NO_CONTEXT);
    std::mutex mutex;
    std::condition_variable condVar;
    enum class Step
    {
        Start,
        Thread1Draw,
        Thread0Delete,
        Thread1Draw2,
        Finish,
        Abort,
    };
    Step currentStep = Step::Start;
    auto waitForStep = [&](Step waitStep) -> bool {
        std::unique_lock<std::mutex> lock(mutex);
        while (currentStep != waitStep)
        {
            if (currentStep == Step::Abort)
            {
                return false;
            }
            condVar.wait(lock);
        }
        return true;
    };
    auto nextStep = [&](Step newStep) {
        {
            std::unique_lock<std::mutex> lock(mutex);
            currentStep = newStep;
        }
        condVar.notify_one();
    };
    class AbortOnFailure
    {
      public:
        AbortOnFailure(Step *currentStep, std::mutex *mutex, std::condition_variable *condVar)
            : mCurrentStep(currentStep), mMutex(mutex), mCondVar(condVar)
        {}
        ~AbortOnFailure()
        {
            bool isAborting = false;
            {
                std::unique_lock<std::mutex> lock(*mMutex);
                isAborting = *mCurrentStep != Step::Finish;
                if (isAborting)
                {
                    *mCurrentStep = Step::Abort;
                }
            }
            mCondVar->notify_all();
        }
      private:
        Step *mCurrentStep;
        std::mutex *mMutex;
        std::condition_variable *mCondVar;
    };
    std::thread deletingThread = std::thread([&]() {
        AbortOnFailure abortOnFailure(&currentStep, &mutex, &condVar);
        EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
        EXPECT_EGL_SUCCESS();
        EXPECT_GL_NO_ERROR();
        ASSERT_TRUE(waitForStep(Step::Thread1Draw));
        SafeDestroyContext(display, context);
        nextStep(Step::Thread0Delete);
        ASSERT_TRUE(waitForStep(Step::Finish));
    });
    std::thread continuingThread = std::thread([&]() {
        EGLContext localContext = context;
        AbortOnFailure abortOnFailure(&currentStep, &mutex, &condVar);
        EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, localContext));
        EXPECT_EGL_SUCCESS();
        constexpr GLsizei kTexSize = 1;
        const GLColor kTexData     = GLColor::red;
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     &kTexData);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        GLProgram program;
        program.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
        ASSERT_TRUE(program.valid());
        drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, localContext));
        EXPECT_EGL_SUCCESS();
        nextStep(Step::Thread1Draw);
        ASSERT_TRUE(waitForStep(Step::Thread0Delete));
        EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, localContext));
        EXPECT_EGL_SUCCESS();
        drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
        nextStep(Step::Finish);
        EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
        EXPECT_EGL_SUCCESS();
    });
    deletingThread.join();
    continuingThread.join();
    ASSERT_NE(currentStep, Step::Abort);
    ASSERT_GL_NO_ERROR();
}
TEST_P ( EGLContextASANTest , DestroyContextInUse ) { ANGLE_SKIP_TEST_IF ( ! platformSupportsMultithreading ( ) ) ; EGLDisplay display = getEGLWindow ( ) -> getDisplay ( ) ; EGLConfig config = getEGLWindow ( ) -> getConfig ( ) ; EGLSurface surface = getEGLWindow ( ) -> getSurface ( ) ; const EGLint contextAttribs [ ] = { EGL_CONTEXT_CLIENT_VERSION , getEGLWindow ( ) -> getClientMajorVersion ( ) , EGL_NONE } ; EGLContext context = eglCreateContext ( display , config , nullptr , contextAttribs ) ; ASSERT_EGL_SUCCESS ( ) ; ASSERT_TRUE ( context != EGL_NO_CONTEXT ) ; std :: mutex mutex ; std :: condition_variable condVar ; enum class Step { Start , Thread1Draw , Thread0Delete , Thread1Draw2 , Finish , Abort , } ; Step currentStep = Step :: Start ; auto waitForStep = [ & ] ( Step waitStep ) -> bool { std :: unique_lock < std :: mutex > lock ( mutex ) ; while ( currentStep != waitStep ) { if ( currentStep == Step :: Abort ) { return false ; } condVar . wait ( lock ) ; } return true ; } ; auto nextStep = [ & ] ( Step newStep ) { { std :: unique_lock < std :: mutex > lock ( mutex ) ; currentStep = newStep ; } condVar . notify_one ( ) ; } ; class AbortOnFailure { public : AbortOnFailure ( Step * currentStep , std :: mutex * mutex , std :: condition_variable * condVar ) : mCurrentStep ( currentStep ) , mMutex ( mutex ) , mCondVar ( condVar ) { } ~ AbortOnFailure ( ) { bool isAborting = false ; { std :: unique_lock < std :: mutex > lock ( * mMutex ) ; isAborting = * mCurrentStep != Step :: Finish ; if ( isAborting ) { * mCurrentStep = Step :: Abort ; } } mCondVar -> notify_all ( ) ; } private : Step * mCurrentStep ; std :: mutex * mMutex ; std :: condition_variable * mCondVar ; } ; std :: thread deletingThread = std :: thread ( [ & ] ( ) { AbortOnFailure abortOnFailure ( & currentStep , & mutex , & condVar ) ; EXPECT_EGL_TRUE ( eglMakeCurrent ( display , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ) ; EXPECT_EGL_SUCCESS ( ) ; EXPECT_GL_NO_ERROR ( ) ; ASSERT_TRUE ( waitForStep ( Step :: Thread1Draw ) ) ; SafeDestroyContext ( display , context ) ; nextStep ( Step :: Thread0Delete ) ; ASSERT_TRUE ( waitForStep ( Step :: Finish ) ) ; } ) ; std :: thread continuingThread = std :: thread ( [ & ] ( ) { EGLContext localContext = context ; AbortOnFailure abortOnFailure ( & currentStep , & mutex , & condVar ) ; EXPECT_EGL_TRUE ( eglMakeCurrent ( display , surface , surface , localContext ) ) ; EXPECT_EGL_SUCCESS ( ) ; constexpr GLsizei kTexSize = 1 ; const GLColor kTexData = GLColor :: red ; GLTexture tex ; glBindTexture ( GL_TEXTURE_2D , tex ) ; glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , kTexSize , kTexSize , 0 , GL_RGBA , GL_UNSIGNED_BYTE , & kTexData ) ; glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ; glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ; GLProgram program ; program . makeRaster ( essl1_shaders :: vs :: Texture2D ( ) , essl1_shaders :: fs :: Texture2D ( ) ) ; ASSERT_TRUE ( program . valid ( ) ) ; drawQuad ( program . get ( ) , essl1_shaders :: PositionAttrib ( ) , 0.5f ) ; EXPECT_EGL_TRUE ( eglMakeCurrent ( display , EGL_NO_SURFACE , EGL_NO_SURFACE , localContext ) ) ; EXPECT_EGL_SUCCESS ( ) ; nextStep ( Step :: Thread1Draw ) ; ASSERT_TRUE ( waitForStep ( Step :: Thread0Delete ) ) ; EXPECT_EGL_TRUE ( eglMakeCurrent ( display , surface , surface , localContext ) ) ; EXPECT_EGL_SUCCESS ( ) ; drawQuad ( program . get ( ) , essl1_shaders :: PositionAttrib ( ) , 0.5f ) ; nextStep ( Step :: Finish ) ; EXPECT_EGL_TRUE ( eglMakeCurrent ( display , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ) ; EXPECT_EGL_SUCCESS ( ) ; } ) ; deletingThread . join ( ) ; continuingThread . join ( ) ; ASSERT_NE ( currentStep , Step :: Abort ) ; ASSERT_GL_NO_ERROR ( ) ; }
Tests that creating resources works after freeing the share context.
Tests that creating resources works after freeing the share context .
// Synchronization tools to ensure the two threads are interleaved as designed by this test. // Helper functions to synchronize the threads so that the operations are executed in the // specific order the test is written for. // If necessary, abort execution as the other thread has encountered a GL error. // Wait for other thread to draw // Delete the context, if implemented properly this is a no-op because the context is // current in another thread. // Wait for the other thread to use context again // Draw using the texture. // Wait for the other thread to delete the context. // Draw again. If the context has been inappropriately deleted in thread0 this will cause a // use-after-free error. // cleanup