refactor(mobile): IOS replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing (#27471)

refactor: replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing
This commit is contained in:
Luis Nachtigall
2026-04-05 22:11:02 +02:00
committed by GitHub
parent 196307bca5
commit 6fcf651d76
3 changed files with 15 additions and 22 deletions
@@ -1,7 +1,12 @@
import Foundation import Foundation
enum ImageProcessing { enum ImageProcessing {
static let queue = DispatchQueue(label: "thumbnail.processing", qos: .userInitiated, attributes: .concurrent) static let queue = {
static let semaphore = DispatchSemaphore(value: ProcessInfo.processInfo.activeProcessorCount * 2) let q = OperationQueue()
q.name = "thumbnail.processing"
q.qualityOfService = .userInitiated
q.maxConcurrentOperationCount = ProcessInfo.processInfo.activeProcessorCount * 2
return q
}()
static let cancelledResult = Result<[String: Int64]?, any Error>.success(nil) static let cancelledResult = Result<[String: Int64]?, any Error>.success(nil)
} }
+7 -16
View File
@@ -4,7 +4,7 @@ import MobileCoreServices
import Photos import Photos
class LocalImageRequest { class LocalImageRequest {
weak var workItem: DispatchWorkItem? weak var operation: Operation?
var isCancelled = false var isCancelled = false
let callback: (Result<[String: Int64]?, any Error>) -> Void let callback: (Result<[String: Int64]?, any Error>) -> Void
@@ -50,7 +50,7 @@ class LocalImageApiImpl: LocalImageApi {
}() }()
func getThumbhash(thumbhash: String, completion: @escaping (Result<[String : Int64], any Error>) -> Void) { func getThumbhash(thumbhash: String, completion: @escaping (Result<[String : Int64], any Error>) -> Void) {
ImageProcessing.queue.async { ImageProcessing.queue.addOperation {
guard let data = Data(base64Encoded: thumbhash) guard let data = Data(base64Encoded: thumbhash)
else { return completion(.failure(PigeonError(code: "", message: "Invalid base64 string: \(thumbhash)", details: nil)))} else { return completion(.failure(PigeonError(code: "", message: "Invalid base64 string: \(thumbhash)", details: nil)))}
@@ -66,16 +66,7 @@ class LocalImageApiImpl: LocalImageApi {
func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, preferEncoded: Bool, completion: @escaping (Result<[String: Int64]?, any Error>) -> Void) { func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, preferEncoded: Bool, completion: @escaping (Result<[String: Int64]?, any Error>) -> Void) {
let request = LocalImageRequest(callback: completion) let request = LocalImageRequest(callback: completion)
let item = DispatchWorkItem { let operation = BlockOperation {
if request.isCancelled {
return completion(ImageProcessing.cancelledResult)
}
ImageProcessing.semaphore.wait()
defer {
ImageProcessing.semaphore.signal()
}
if request.isCancelled { if request.isCancelled {
return completion(ImageProcessing.cancelledResult) return completion(ImageProcessing.cancelledResult)
} }
@@ -180,9 +171,9 @@ class LocalImageApiImpl: LocalImageApi {
} }
} }
request.workItem = item request.operation = operation
Self.add(requestId: requestId, request: request) Self.add(requestId: requestId, request: request)
ImageProcessing.queue.async(execute: item) ImageProcessing.queue.addOperation(operation)
} }
func cancelRequest(requestId: Int64) { func cancelRequest(requestId: Int64) {
@@ -201,8 +192,8 @@ class LocalImageApiImpl: LocalImageApi {
requestQueue.async { requestQueue.async {
guard let request = requests.removeValue(forKey: requestId) else { return } guard let request = requests.removeValue(forKey: requestId) else { return }
request.isCancelled = true request.isCancelled = true
guard let item = request.workItem else { return } guard let operation = request.operation else { return }
if item.isCancelled { if operation.isCancelled {
cancelQueue.async { request.callback(ImageProcessing.cancelledResult) } cancelQueue.async { request.callback(ImageProcessing.cancelledResult) }
} }
} }
@@ -73,10 +73,7 @@ class RemoteImageApiImpl: NSObject, RemoteImageApi {
return request.completion(.failure(PigeonError(code: "", message: "No data received", details: nil))) return request.completion(.failure(PigeonError(code: "", message: "No data received", details: nil)))
} }
ImageProcessing.queue.async { ImageProcessing.queue.addOperation {
ImageProcessing.semaphore.wait()
defer { ImageProcessing.semaphore.signal() }
if request.isCancelled { if request.isCancelled {
return request.completion(ImageProcessing.cancelledResult) return request.completion(ImageProcessing.cancelledResult)
} }