mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
Fix MediaLoader EXC_BAD_ACCESS ongoingRequests crashes
This commit is contained in:
@@ -12,10 +12,6 @@ import UIKit
|
||||
|
||||
@preconcurrency import MatrixRustSDK
|
||||
|
||||
private final nonisolated class MediaRequest {
|
||||
var continuations: [CheckedContinuation<Data, Error>] = []
|
||||
}
|
||||
|
||||
actor MediaLoader: MediaLoaderProtocol {
|
||||
// We noticed that the keyboard appears to hold onto a reference to the `Context` of the last
|
||||
// screen that had text input focus, resulting in its MediaProvider staying alive which in
|
||||
@@ -28,7 +24,7 @@ actor MediaLoader: MediaLoaderProtocol {
|
||||
// bunch of workarounds in our preview tests to keep the mock provider alive as some ViewModels
|
||||
// don't have an accompanying ClientMock to own it.
|
||||
private weak var client: ClientProtocol?
|
||||
private var ongoingRequests = [MediaSourceProxy: MediaRequest]()
|
||||
private var ongoingRequests = [MediaSourceProxy: Task<Data, Error>]()
|
||||
|
||||
init(client: ClientProtocol) {
|
||||
self.client = client
|
||||
@@ -61,30 +57,22 @@ actor MediaLoader: MediaLoaderProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func enqueueLoadMediaRequest(forSource source: MediaSourceProxy, operation: @escaping () async throws -> Data) async throws -> Data {
|
||||
private func enqueueLoadMediaRequest(forSource source: MediaSourceProxy, operation: @escaping @Sendable () async throws -> Data) async throws -> Data {
|
||||
if let ongoingRequest = ongoingRequests[source] {
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
ongoingRequest.continuations.append(continuation)
|
||||
}
|
||||
return try await ongoingRequest.value
|
||||
}
|
||||
|
||||
let ongoingRequest = MediaRequest()
|
||||
// Wrap the SDK call in a Task so `ongoingRequests` is only mutated when resuming from
|
||||
// `task.value` (a runtime-managed await that hops back onto the actor) rather than when
|
||||
// resuming directly from the SDK's continuation, which can fire on a Rust thread and
|
||||
// mutate the dictionary off-actor, corrupting its storage.
|
||||
let ongoingRequest = Task { try await operation() }
|
||||
ongoingRequests[source] = ongoingRequest
|
||||
|
||||
defer {
|
||||
ongoingRequests[source] = nil
|
||||
}
|
||||
|
||||
do {
|
||||
let result = try await operation()
|
||||
|
||||
ongoingRequest.continuations.forEach { $0.resume(returning: result) }
|
||||
|
||||
return result
|
||||
|
||||
} catch {
|
||||
ongoingRequest.continuations.forEach { $0.resume(throwing: error) }
|
||||
throw error
|
||||
}
|
||||
return try await ongoingRequest.value
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user