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