Fix ANRs when shutting down the engine
This commit is contained in:
@@ -108,6 +108,8 @@ class Godot private constructor(val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private const val EXIT_RENDERER_TIMEOUT_IN_MS = 1500L
|
||||
|
||||
// Supported build flavors
|
||||
private const val EDITOR_FLAVOR = "editor"
|
||||
private const val TEMPLATE_FLAVOR = "template"
|
||||
@@ -748,7 +750,12 @@ class Godot private constructor(val context: Context) {
|
||||
plugin.onMainDestroy()
|
||||
}
|
||||
|
||||
renderView?.onActivityDestroyed()
|
||||
if (renderView?.blockingExitRenderer(EXIT_RENDERER_TIMEOUT_IN_MS) != true) {
|
||||
Log.w(TAG, "Unable to exit the renderer within $EXIT_RENDERER_TIMEOUT_IN_MS ms... Force quitting the process.")
|
||||
onGodotTerminating()
|
||||
forceQuit(0)
|
||||
}
|
||||
|
||||
this.primaryHost = null
|
||||
}
|
||||
|
||||
|
||||
@@ -132,8 +132,8 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed() {
|
||||
requestRenderThreadExitAndWait();
|
||||
public boolean blockingExitRenderer(long blockingTimeInMs) {
|
||||
return requestRenderThreadExitAndWait(blockingTimeInMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,7 +55,7 @@ public interface GodotRenderView {
|
||||
|
||||
void onActivityStarted();
|
||||
|
||||
void onActivityDestroyed();
|
||||
boolean blockingExitRenderer(long blockingTimeInMs);
|
||||
|
||||
GodotInputHandler getInputHandler();
|
||||
|
||||
|
||||
@@ -117,8 +117,8 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed() {
|
||||
requestRenderThreadExitAndWait();
|
||||
public boolean blockingExitRenderer(long blockingTimeInMs) {
|
||||
return requestRenderThreadExitAndWait(blockingTimeInMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -604,6 +604,18 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
||||
mGLThread.requestExitAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the render thread to exit and block up to the given timeInMs until it's done.
|
||||
*
|
||||
* @return true if the thread exited, false otherwise.
|
||||
*/
|
||||
protected final boolean requestRenderThreadExitAndWait(long timeInMs) {
|
||||
if (mGLThread != null) {
|
||||
return mGLThread.requestExitAndWait(timeInMs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// -- GODOT end --
|
||||
|
||||
/**
|
||||
@@ -1796,6 +1808,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
||||
}
|
||||
}
|
||||
|
||||
public boolean requestExitAndWait(long timeInMs) {
|
||||
// Don't call this from GLThread thread or it is a guaranteed deadlock!
|
||||
synchronized(sGLThreadManager) {
|
||||
mShouldExit = true;
|
||||
sGLThreadManager.notifyAll();
|
||||
if (!mExited) {
|
||||
try {
|
||||
sGLThreadManager.wait(timeInMs);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
return mExited;
|
||||
}
|
||||
}
|
||||
|
||||
public void requestReleaseEglContextLocked() {
|
||||
mShouldReleaseEglContext = true;
|
||||
sGLThreadManager.notifyAll();
|
||||
|
||||
@@ -119,6 +119,15 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf
|
||||
vkThread.requestExitAndWait()
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the render thread to exit and block up to the given [timeInMs] until it's done.
|
||||
*
|
||||
* @return true if the thread exited, false otherwise.
|
||||
*/
|
||||
fun requestRenderThreadExitAndWait(timeInMs: Long): Boolean {
|
||||
return vkThread.requestExitAndWait(timeInMs)
|
||||
}
|
||||
|
||||
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||
vkThread.onSurfaceChanged(width, height)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
package org.godotengine.godot.vulkan
|
||||
|
||||
import android.util.Log
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
@@ -104,13 +105,37 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
|
||||
try {
|
||||
Log.i(TAG, "Waiting on exit for $name")
|
||||
lockCondition.await()
|
||||
} catch (ex: InterruptedException) {
|
||||
} catch (_: InterruptedException) {
|
||||
currentThread().interrupt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the thread to exit and block up to the given [timeInMs] until it's done.
|
||||
*
|
||||
* @return true if the thread exited, false otherwise.
|
||||
*/
|
||||
fun requestExitAndWait(timeInMs: Long): Boolean {
|
||||
lock.withLock {
|
||||
shouldExit = true
|
||||
lockCondition.signalAll()
|
||||
|
||||
var remainingTimeInNanos = TimeUnit.MILLISECONDS.toNanos(timeInMs)
|
||||
while (!exited && remainingTimeInNanos > 0) {
|
||||
try {
|
||||
Log.i(TAG, "Waiting on exit for $name for $remainingTimeInNanos")
|
||||
remainingTimeInNanos = lockCondition.awaitNanos(remainingTimeInNanos)
|
||||
} catch (_: InterruptedException) {
|
||||
currentThread().interrupt()
|
||||
}
|
||||
}
|
||||
|
||||
return exited
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the app resumes.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user