Absturz beim Abspielen, und die Annahme dahinter

Der Crashlog vom Gerät:

    _dispatch_assert_queue_fail
    _swift_task_checkIsolatedSwift
    MainActor.assumeIsolated
    VLCPlayerEngine.mediaPlayerStateChanged(_:)
    VLCKit input_thread_Events

Im Code stand als Begründung für assumeIsolated, VLCKit stelle seine
Rückrufe über die Hauptwarteschlange zu. Das ist falsch — es ruft den
Delegaten von seinem eigenen Input-Thread. Die App brach deshalb hart
ab, sobald VLC den ersten Zustandswechsel meldete, also unmittelbar
beim Drücken auf Play.

Alle drei Delegat-Methoden springen jetzt mit Task { @MainActor in }
sauber hinüber. Die Reihenfolge bleibt erhalten: die Aufgaben werden aus
demselben Thread nacheinander eingereiht.

Dieselbe Annahme steckte im AVPlayer-Motor in den KVO-Beobachtern —
KVO meldet auf dem Thread, der die Änderung ausgelöst hat, und AVPlayer
ändert seinen Zustand auch aus internen Threads. Ebenfalls umgestellt.
Die beiden Rückrufe mit ausdrücklichem queue: .main behalten
assumeIsolated: dort ist die Annahme belegt statt geraten, und der
Zeitbeobachter feuert viermal je Sekunde.

Dazu die Anmeldung: Plex führt den Rücksprung über forwardUrl nicht
zuverlässig aus. Auf dem iPad landet der Browser nach dem Anmelden auf
watch.plex.tv und bleibt stehen — der Token liegt bereit, aber die App
wartete auf einen Rückruf, der nie kam. Das Fenster wird jetzt geöffnet,
ohne darauf zu warten; parallel wird im Sekundentakt beim Server
nachgefragt. Der Rücksprung ist damit eine Annehmlichkeit, keine
Voraussetzung.

132 Tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-09-11 21:25:17 +02:00
co-authored by Claude Opus 5
parent d9ae4b8a1f
commit a2b3f901ed
6 changed files with 410 additions and 290 deletions
+238 -212
View File
@@ -471,6 +471,222 @@
}
}
},
"diagnostics.connection": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Verbindung"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Connection"
}
}
}
},
"diagnostics.connectionUsed": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Verbindung"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Connection"
}
}
}
},
"diagnostics.copy": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Bericht kopieren"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Copy report"
}
}
}
},
"diagnostics.copyHint": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Enthält keinen Token. Kannst du unbesorgt weitergeben."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Contains no token. Safe to share."
}
}
}
},
"diagnostics.lastAttempt": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Letzter Abspielversuch"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Last playback attempt"
}
}
}
},
"diagnostics.method": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Weg"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Method"
}
}
}
},
"diagnostics.noServer": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Keine Serververbindung."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "No server connection."
}
}
}
},
"diagnostics.nothingYet": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Noch nichts abgespielt."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Nothing played yet."
}
}
}
},
"diagnostics.relayWarning": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Über die Plex-Vermittlung. Stark gedrosselt — große Dateien laufen darüber kaum an."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Via the Plex relay. Heavily throttled — large files barely start over it."
}
}
}
},
"diagnostics.source": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Quelle (ohne Token)"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Source (token removed)"
}
}
}
},
"diagnostics.steps": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Ablauf"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Timeline"
}
}
}
},
"diagnostics.title": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Diagnose"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Diagnostics"
}
}
}
},
"diagnostics.title.label": {
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Titel"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Title"
}
}
}
},
"downloads.active": {
"extractionState": "stale",
"localizations": {
@@ -2117,6 +2333,24 @@
}
}
},
"settings.build": {
"comment": "Zeitpunkt, zu dem der laufende Build entstanden ist",
"extractionState": "stale",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Build"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Build"
}
}
}
},
"settings.connection": {
"extractionState": "stale",
"localizations": {
@@ -3180,227 +3414,19 @@
}
}
},
"settings.build": {
"comment": "Zeitpunkt, zu dem der laufende Build entstanden ist",
"signIn.timedOut": {
"comment": "Die Anmeldung wurde nicht innerhalb der Gültigkeit des Codes abgeschlossen",
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Build"
"value": "Die Anmeldung wurde nicht abgeschlossen. Versuch es noch einmal."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Build"
}
}
}
},
"diagnostics.title": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Diagnose"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Diagnostics"
}
}
}
},
"diagnostics.connection": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Verbindung"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Connection"
}
}
}
},
"diagnostics.noServer": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Keine Serververbindung."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "No server connection."
}
}
}
},
"diagnostics.relayWarning": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Über die Plex-Vermittlung. Stark gedrosselt — große Dateien laufen darüber kaum an."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Via the Plex relay. Heavily throttled — large files barely start over it."
}
}
}
},
"diagnostics.lastAttempt": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Letzter Abspielversuch"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Last playback attempt"
}
}
}
},
"diagnostics.nothingYet": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Noch nichts abgespielt."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Nothing played yet."
}
}
}
},
"diagnostics.title.label": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Titel"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Title"
}
}
}
},
"diagnostics.method": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Weg"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Method"
}
}
}
},
"diagnostics.connectionUsed": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Verbindung"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Connection"
}
}
}
},
"diagnostics.source": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Quelle (ohne Token)"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Source (token removed)"
}
}
}
},
"diagnostics.steps": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Ablauf"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Timeline"
}
}
}
},
"diagnostics.copy": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Bericht kopieren"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Copy report"
}
}
}
},
"diagnostics.copyHint": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Enthält keinen Token. Kannst du unbesorgt weitergeben."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Contains no token. Safe to share."
"value": "Sign-in wasn't completed. Please try again."
}
}
}