• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     std::thread deletingThread = std::thread([&]() {
126         AbortOnFailure abortOnFailure(&currentStep, &mutex, &condVar);
127 
128         EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
129         EXPECT_EGL_SUCCESS();
130         EXPECT_GL_NO_ERROR();
131 
132         // Wait for other thread to draw
133         ASSERT_TRUE(waitForStep(Step::Thread1Draw));
134 
135         // Delete the context, if implemented properly this is a no-op because the context is
136         // current in another thread.
137         SafeDestroyContext(display, context);
138 
139         // Wait for the other thread to use context again
140         nextStep(Step::Thread0Delete);
141         ASSERT_TRUE(waitForStep(Step::Finish));
142     });
143 
144     std::thread continuingThread = std::thread([&]() {
145         EGLContext localContext = context;
146         AbortOnFailure abortOnFailure(&currentStep, &mutex, &condVar);
147 
148         EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, localContext));
149         EXPECT_EGL_SUCCESS();
150 
151         constexpr GLsizei kTexSize = 1;
152         const GLColor kTexData     = GLColor::red;
153 
154         GLTexture tex;
155         glBindTexture(GL_TEXTURE_2D, tex);
156         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
157                      &kTexData);
158         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
159         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
160 
161         GLProgram program;
162         program.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
163         ASSERT_TRUE(program.valid());
164 
165         // Draw using the texture.
166         drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
167 
168         EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, localContext));
169         EXPECT_EGL_SUCCESS();
170 
171         // Wait for the other thread to delete the context.
172         nextStep(Step::Thread1Draw);
173         ASSERT_TRUE(waitForStep(Step::Thread0Delete));
174 
175         EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, localContext));
176         EXPECT_EGL_SUCCESS();
177 
178         // Draw again. If the context has been inappropriately deleted in thread0 this will cause a
179         // use-after-free error.
180         drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
181 
182         nextStep(Step::Finish);
183 
184         EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
185         EXPECT_EGL_SUCCESS();
186     });
187 
188     deletingThread.join();
189     continuingThread.join();
190 
191     ASSERT_NE(currentStep, Step::Abort);
192 
193     // cleanup
194     ASSERT_GL_NO_ERROR();
195 }
196 }  // anonymous namespace
197 
198 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextASANTest);
199 ANGLE_INSTANTIATE_TEST(EGLContextASANTest,
200                        ES2_D3D9(),
201                        ES2_D3D11(),
202                        ES3_D3D11(),
203                        ES2_OPENGL(),
204                        ES3_OPENGL(),
205                        ES2_VULKAN(),
206                        ES3_VULKAN());
207