xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLContextASANTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2020 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 // EGLContextASANTest.cpp:
7 //   Tests relating to ASAN errors regarding context.
8 
9 #include <gtest/gtest.h>
10 
11 #include "test_utils/ANGLETest.h"
12 #include "test_utils/angle_test_configs.h"
13 #include "test_utils/gl_raii.h"
14 #include "util/EGLWindow.h"
15 
16 #include <condition_variable>
17 #include <mutex>
18 #include <thread>
19 
20 using namespace angle;
21 
22 namespace
23 {
24 
SafeDestroyContext(EGLDisplay display,EGLContext & context)25 EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)
26 {
27     EGLBoolean result = EGL_TRUE;
28     if (context != EGL_NO_CONTEXT)
29     {
30         result  = eglDestroyContext(display, context);
31         context = EGL_NO_CONTEXT;
32     }
33     return result;
34 }
35 
36 class EGLContextASANTest : public ANGLETest<>
37 {
38   public:
EGLContextASANTest()39     EGLContextASANTest() {}
40 };
41 
42 // Tests that creating resources works after freeing the share context.
TEST_P(EGLContextASANTest,DestroyContextInUse)43 TEST_P(EGLContextASANTest, DestroyContextInUse)
44 {
45     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
46 
47     EGLDisplay display = getEGLWindow()->getDisplay();
48     EGLConfig config   = getEGLWindow()->getConfig();
49     EGLSurface surface = getEGLWindow()->getSurface();
50 
51     const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,
52                                      getEGLWindow()->getClientMajorVersion(), EGL_NONE};
53 
54     EGLContext context = eglCreateContext(display, config, nullptr, contextAttribs);
55     ASSERT_EGL_SUCCESS();
56     ASSERT_TRUE(context != EGL_NO_CONTEXT);
57 
58     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
59     std::mutex mutex;
60     std::condition_variable condVar;
61 
62     enum class Step
63     {
64         Start,
65         Thread1Draw,
66         Thread0Delete,
67         Thread1Draw2,
68         Finish,
69         Abort,
70     };
71     Step currentStep = Step::Start;
72 
73     // Helper functions to synchronize the threads so that the operations are executed in the
74     // specific order the test is written for.
75     auto waitForStep = [&](Step waitStep) -> bool {
76         std::unique_lock<std::mutex> lock(mutex);
77         while (currentStep != waitStep)
78         {
79             // If necessary, abort execution as the other thread has encountered a GL error.
80             if (currentStep == Step::Abort)
81             {
82                 return false;
83             }
84             condVar.wait(lock);
85         }
86 
87         return true;
88     };
89     auto nextStep = [&](Step newStep) {
90         {
91             std::unique_lock<std::mutex> lock(mutex);
92             currentStep = newStep;
93         }
94         condVar.notify_one();
95     };
96 
97     class AbortOnFailure
98     {
99       public:
100         AbortOnFailure(Step *currentStep, std::mutex *mutex, std::condition_variable *condVar)
101             : mCurrentStep(currentStep), mMutex(mutex), mCondVar(condVar)
102         {}
103 
104         ~AbortOnFailure()
105         {
106             bool isAborting = false;
107             {
108                 std::unique_lock<std::mutex> lock(*mMutex);
109                 isAborting = *mCurrentStep != Step::Finish;
110 
111                 if (isAborting)
112                 {
113                     *mCurrentStep = Step::Abort;
114                 }
115             }
116             mCondVar->notify_all();
117         }
118 
119       private:
120         Step *mCurrentStep;
121         std::mutex *mMutex;
122         std::condition_variable *mCondVar;
123     };
124 
125     // Unmake current to release the "surface".
126     EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
127     EXPECT_EGL_SUCCESS();
128 
129     std::thread deletingThread = std::thread([&]() {
130         AbortOnFailure abortOnFailure(&currentStep, &mutex, &condVar);
131 
132         EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
133         EXPECT_EGL_SUCCESS();
134         EXPECT_GL_NO_ERROR();
135 
136         // Wait for other thread to draw
137         ASSERT_TRUE(waitForStep(Step::Thread1Draw));
138 
139         // Delete the context, if implemented properly this is a no-op because the context is
140         // current in another thread.
141         SafeDestroyContext(display, context);
142 
143         // Wait for the other thread to use context again
144         nextStep(Step::Thread0Delete);
145         ASSERT_TRUE(waitForStep(Step::Finish));
146     });
147 
148     std::thread continuingThread = std::thread([&]() {
149         EGLContext localContext = context;
150         AbortOnFailure abortOnFailure(&currentStep, &mutex, &condVar);
151 
152         EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, localContext));
153         EXPECT_EGL_SUCCESS();
154 
155         constexpr GLsizei kTexSize = 1;
156         const GLColor kTexData     = GLColor::red;
157 
158         GLTexture tex;
159         glBindTexture(GL_TEXTURE_2D, tex);
160         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
161                      &kTexData);
162         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
163         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
164 
165         GLProgram program;
166         program.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
167         ASSERT_TRUE(program.valid());
168 
169         // Draw using the texture.
170         drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
171 
172         EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, localContext));
173         EXPECT_EGL_SUCCESS();
174 
175         // Wait for the other thread to delete the context.
176         nextStep(Step::Thread1Draw);
177         ASSERT_TRUE(waitForStep(Step::Thread0Delete));
178 
179         EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, localContext));
180         EXPECT_EGL_SUCCESS();
181 
182         // Draw again. If the context has been inappropriately deleted in thread0 this will cause a
183         // use-after-free error.
184         drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
185 
186         nextStep(Step::Finish);
187 
188         EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
189         EXPECT_EGL_SUCCESS();
190     });
191 
192     deletingThread.join();
193     continuingThread.join();
194 
195     ASSERT_NE(currentStep, Step::Abort);
196 
197     // Make default context and surface current again.
198     EXPECT_TRUE(getEGLWindow()->makeCurrent());
199     EXPECT_EGL_SUCCESS();
200 
201     // cleanup
202     ASSERT_GL_NO_ERROR();
203 }
204 }  // anonymous namespace
205 
206 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextASANTest);
207 ANGLE_INSTANTIATE_TEST(EGLContextASANTest,
208                        ES2_D3D9(),
209                        ES2_D3D11(),
210                        ES3_D3D11(),
211                        ES2_OPENGL(),
212                        ES3_OPENGL(),
213                        ES2_VULKAN(),
214                        ES3_VULKAN());
215