Fix reentrant deadlock in GLSurfaceView.
It is possible for clients to call SurfaceView
methods e.g. setFormat from the GLSurfaceView#Renderer
drawFrame callback. In this case, SurfaceView#updatewindow
will understand that a redraw is required, and will request
it back to GLSurfaceView. Remember, we are still on the GLThread
though (from the drawFrame callback). So these methods
(requestRenderAndWait, windowSizeChanged), need to be reentrant.
Also fix a data race around mWantRenderNotification, which was
accessed out of lock.
Bug: 26770615
Change-Id: Ic66a36c886ae0b085dd456a4220f0d4f270fb016
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 9c01f4f..f37ec58 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -1291,6 +1291,7 @@
boolean createGlInterface = false;
boolean lostEglContext = false;
boolean sizeChanged = false;
+ boolean wantRenderNotification = false;
boolean doRenderNotification = false;
boolean askedToReleaseEglContext = false;
int w = 0;
@@ -1448,6 +1449,9 @@
}
mRequestRender = false;
sGLThreadManager.notifyAll();
+ if (mWantRenderNotification) {
+ wantRenderNotification = true;
+ }
break;
}
}
@@ -1574,8 +1578,9 @@
break;
}
- if (mWantRenderNotification) {
+ if (wantRenderNotification) {
doRenderNotification = true;
+ wantRenderNotification = false;
}
}
@@ -1625,11 +1630,21 @@
public void requestRenderAndWait() {
synchronized(sGLThreadManager) {
+ // If we are already on the GL thread, this means a client callback
+ // has caused reentrancy, for example via updating the SurfaceView parameters.
+ // We will return to the client rendering code, so here we don't need to
+ // do anything.
+ if (Thread.currentThread() == this) {
+ return;
+ }
+
mWantRenderNotification = true;
mRequestRender = true;
mRenderComplete = false;
+
sGLThreadManager.notifyAll();
- while (!mExited && !mPaused && mRenderComplete == false) {
+
+ while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) {
try {
sGLThreadManager.wait();
} catch (InterruptedException ex) {
@@ -1726,6 +1741,16 @@
mSizeChanged = true;
mRequestRender = true;
mRenderComplete = false;
+
+ // If we are already on the GL thread, this means a client callback
+ // has caused reentrancy, for example via updating the SurfaceView parameters.
+ // We need to process the size change eventually though and update our EGLSurface.
+ // So we set the parameters and return so they can be processed on our
+ // next iteration.
+ if (Thread.currentThread() == this) {
+ return;
+ }
+
sGLThreadManager.notifyAll();
// Wait for thread to react to resize and render a frame