Skip to content

Commit c9fc96f

Browse files
committed
Fix ANRs when shutting down the engine
1 parent 1559ab3 commit c9fc96f

File tree

7 files changed

+77
-7
lines changed

7 files changed

+77
-7
lines changed

platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class Godot private constructor(val context: Context) {
108108
}
109109
}
110110

111+
private const val EXIT_RENDERER_TIMEOUT_IN_MS = 750L
112+
111113
// Supported build flavors
112114
private const val EDITOR_FLAVOR = "editor"
113115
private const val TEMPLATE_FLAVOR = "template"
@@ -748,7 +750,12 @@ class Godot private constructor(val context: Context) {
748750
plugin.onMainDestroy()
749751
}
750752

751-
renderView?.onActivityDestroyed()
753+
if (renderView?.blockingExitRenderer(EXIT_RENDERER_TIMEOUT_IN_MS) != true) {
754+
Log.w(TAG, "Unable to exit the renderer within $EXIT_RENDERER_TIMEOUT_IN_MS ms... Force quitting the process.")
755+
onGodotTerminating()
756+
forceQuit(0)
757+
}
758+
752759
this.primaryHost = null
753760
}
754761

platform/android/java/lib/src/main/java/org/godotengine/godot/GodotGLRenderView.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ public void onActivityStarted() {
132132
}
133133

134134
@Override
135-
public void onActivityDestroyed() {
136-
requestRenderThreadExitAndWait();
135+
public boolean blockingExitRenderer(long blockingTimeInMs) {
136+
return requestRenderThreadExitAndWait(blockingTimeInMs);
137137
}
138138

139139
@Override

platform/android/java/lib/src/main/java/org/godotengine/godot/GodotRenderView.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public interface GodotRenderView {
5555

5656
void onActivityStarted();
5757

58-
void onActivityDestroyed();
58+
boolean blockingExitRenderer(long blockingTimeInMs);
5959

6060
GodotInputHandler getInputHandler();
6161

platform/android/java/lib/src/main/java/org/godotengine/godot/GodotVulkanRenderView.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ public void onActivityResumed() {
117117
}
118118

119119
@Override
120-
public void onActivityDestroyed() {
121-
requestRenderThreadExitAndWait();
120+
public boolean blockingExitRenderer(long blockingTimeInMs) {
121+
return requestRenderThreadExitAndWait(blockingTimeInMs);
122122
}
123123

124124
@Override

platform/android/java/lib/src/main/java/org/godotengine/godot/gl/GLSurfaceView.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,18 @@ protected final void requestRenderThreadExitAndWait() {
604604
mGLThread.requestExitAndWait();
605605
}
606606
}
607+
608+
/**
609+
* Requests the render thread to exit and block up to the given timeInMs until it's done.
610+
*
611+
* @return true if the thread exited, false otherwise.
612+
*/
613+
protected final boolean requestRenderThreadExitAndWait(long timeInMs) {
614+
if (mGLThread != null) {
615+
return mGLThread.requestExitAndWait(timeInMs);
616+
}
617+
return false;
618+
}
607619
// -- GODOT end --
608620

609621
/**
@@ -1796,6 +1808,23 @@ public void requestExitAndWait() {
17961808
}
17971809
}
17981810

1811+
public boolean requestExitAndWait(long timeInMs) {
1812+
// Don't call this from GLThread thread or it is a guaranteed deadlock!
1813+
synchronized(sGLThreadManager) {
1814+
mShouldExit = true;
1815+
sGLThreadManager.notifyAll();
1816+
if (!mExited) {
1817+
try {
1818+
sGLThreadManager.wait(timeInMs);
1819+
} catch (InterruptedException ex) {
1820+
Thread.currentThread().interrupt();
1821+
}
1822+
}
1823+
1824+
return mExited;
1825+
}
1826+
}
1827+
17991828
public void requestReleaseEglContextLocked() {
18001829
mShouldReleaseEglContext = true;
18011830
sGLThreadManager.notifyAll();

platform/android/java/lib/src/main/java/org/godotengine/godot/vulkan/VkSurfaceView.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf
119119
vkThread.requestExitAndWait()
120120
}
121121

122+
/**
123+
* Requests the render thread to exit and block up to the given [timeInMs] until it's done.
124+
*
125+
* @return true if the thread exited, false otherwise.
126+
*/
127+
fun requestRenderThreadExitAndWait(timeInMs: Long): Boolean {
128+
return vkThread.requestExitAndWait(timeInMs)
129+
}
130+
122131
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
123132
vkThread.onSurfaceChanged(width, height)
124133
}

platform/android/java/lib/src/main/java/org/godotengine/godot/vulkan/VkThread.kt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
package org.godotengine.godot.vulkan
3333

3434
import android.util.Log
35+
import java.util.concurrent.TimeUnit
3536
import java.util.concurrent.locks.ReentrantLock
3637
import kotlin.concurrent.withLock
3738

@@ -104,13 +105,37 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
104105
try {
105106
Log.i(TAG, "Waiting on exit for $name")
106107
lockCondition.await()
107-
} catch (ex: InterruptedException) {
108+
} catch (_: InterruptedException) {
108109
currentThread().interrupt()
109110
}
110111
}
111112
}
112113
}
113114

115+
/**
116+
* Request the thread to exit and block up to the given [timeInMs] until it's done.
117+
*
118+
* @return true if the thread exited, false otherwise.
119+
*/
120+
fun requestExitAndWait(timeInMs: Long): Boolean {
121+
lock.withLock {
122+
shouldExit = true
123+
lockCondition.signalAll()
124+
125+
var remainingTimeInNanos = TimeUnit.MILLISECONDS.toNanos(timeInMs)
126+
while (!exited && remainingTimeInNanos > 0) {
127+
try {
128+
Log.i(TAG, "Waiting on exit for $name for $remainingTimeInNanos")
129+
remainingTimeInNanos = lockCondition.awaitNanos(remainingTimeInNanos)
130+
} catch (_: InterruptedException) {
131+
currentThread().interrupt()
132+
}
133+
}
134+
135+
return exited
136+
}
137+
}
138+
114139
/**
115140
* Invoked when the app resumes.
116141
*/

0 commit comments

Comments
 (0)