TeraMediaWorkQueue.swift (1438B)
1 import Foundation 2 3 /// Cancelled work retains its physical slot until its operation returns. 4 @MainActor 5 final class TeraMediaWorkQueue { 6 private struct Pending { 7 let id: UUID 8 let operation: @MainActor () async -> Void 9 } 10 11 private let limits: TeraMediaPresentationLimits 12 private var active: [UUID: Task<Void, Never>] = [:] 13 private var pending: [Pending] = [] 14 15 init(limits: TeraMediaPresentationLimits) { 16 self.limits = limits 17 } 18 19 deinit { for task in active.values { 20 task.cancel() 21 } } 22 23 var activeCount: Int { 24 active.count 25 } 26 27 var queuedCount: Int { 28 pending.count 29 } 30 31 func submit(id: UUID, operation: @escaping @MainActor () async -> Void) -> Bool { 32 guard active[id] == nil, !pending.contains(where: { $0.id == id }), 33 active.count < limits.workers || pending.count < limits.queuedRequests else { return false } 34 pending.append(Pending(id: id, operation: operation)) 35 drain() 36 return true 37 } 38 39 func cancel(id: UUID) { 40 pending.removeAll { $0.id == id } 41 active[id]?.cancel() 42 } 43 44 func cancelAll() { 45 pending.removeAll() 46 for task in active.values { 47 task.cancel() 48 } 49 } 50 51 private func drain() { 52 while active.count < limits.workers, !pending.isEmpty { 53 let work = pending.removeFirst() 54 active[work.id] = Task { [weak self] in 55 await work.operation() 56 self?.active[work.id] = nil 57 self?.drain() 58 } 59 } 60 } 61 }