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(¤tStep, &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(¤tStep, &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