From ee323345f0b4d469b0d4d00d6edc77cbd63b98da Mon Sep 17 00:00:00 2001 From: x270880x Date: Wed, 28 Oct 2026 11:41:53 +0200 Subject: [PATCH] Copy multi-preview surfaces with ArrayList instead of toList() draw() snapshots multiPreviewSurfaceManagers with values.toList(). For a single-element collection Kotlin's toList() reads size and then calls iterator().next() as a separate step. If that surface is removed from another thread in between (removeMultiPreviewSurface() on the main thread while draw() runs on the GL thread), next() throws NoSuchElementException on the GL thread, and without a render error callback the app crashes. ArrayList(values) copies through ConcurrentHashMap's toArray(), which tolerates concurrent changes, so a surface removed meanwhile is simply in or out of this frame's snapshot, as it already is with two or more. Co-Authored-By: Claude Opus 5.5 --- .../src/main/java/com/pedro/library/view/GlStreamInterface.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt b/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt index fd318e227..3c97b53cc 100644 --- a/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt +++ b/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt @@ -348,7 +348,9 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, surfaceManager.swapBuffer() } } - val previewSnapshot = multiPreviewSurfaceManagers.values.toList() + // Not toList(): for one element it calls iterator().next(), which throws NoSuchElementException + // if that surface is removed from another thread in between. ArrayList copies via toArray(), which can't. + val previewSnapshot = ArrayList(multiPreviewSurfaceManagers.values) previewSnapshot.forEach { info -> if (info.surfaceManager.isReady) { if (info.surfaceManager.makeCurrent()) {