Block native teardown until viewer and shared mixer cleanup is confirmed

This commit is contained in:
evgeny committed 2026-10-29 13:34:24 +01:00
1 parent 16c7d817cc
commit 495d399691
3 files changed
+22

No files matched your search

@@ -74,6 +74,7 @@ object NativeLib {
RadioCallController.reset()
StreamController.reset(); CallController.reset(); ChatApplication.instance.avatars.stop(); ChatApplication.instance.memberFiles.cancel()
CallService.requireReleased() // Не останавливать native при неподтверждённой очистке его media-владельца.
StreamPlayerEngine.stopForCore()
check(!CallController.blocksMedia()) {
LogManager.addLog("ERROR", "Call", "Core teardown blocked: call media preparation still running")
"Call media preparation still running"
@@ -49,6 +49,14 @@ internal object StreamAudioMixer {
LogManager.addLog("INFO", "stream", "shared audio output released for call")
}
/** Проверка core teardown без join: живой worker удерживает Android output для retry. */
fun requireReleased() = synchronized(lock) {
check(inputs.isEmpty() && worker?.isAlive != true) {
LogManager.addLog("ERROR", "stream", "core teardown blocked by mixer inputs=${inputs.size} worker=${worker?.state}")
"Stream mixer cleanup pending"
}
}
private fun run() {
var audio: StreamAudioOutput? = null
var generated = 0L
@@ -24,6 +24,19 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
private val mutableViewing = MutableStateFlow(false)
val viewing: StateFlow<Boolean> = mutableViewing
/** Main: запросить stop всех viewers; незавершённый cleanup запрещает native stop и допускает повтор. */
fun stopForCore() {
check(Looper.myLooper() == Looper.getMainLooper()) { "Viewer core teardown requires main" }
players.toList().forEach { it.stop() }
players.removeAll { player -> synchronized(player.releaseLock) { player.released } && player.worker?.isAlive != true }
mutableViewing.value = players.isNotEmpty()
check(players.isEmpty()) {
LogManager.addLog("ERROR", "stream", "core teardown blocked by viewers count=${players.size}; cleanup retained")
"Stream viewers cleanup pending"
}
StreamAudioMixer.requireReleased()
}
/** Закрыть просмотр и дождаться освобождения AudioTrack/decoder вне main thread. */
suspend fun stopForCall() {
val stopped = players.toList()