Собственная concurrent queue с barrier сериализует запись относительно чтений. Но вызов completion внутри очереди создаёт риск reentrancy и deadlock при setValue из callback.
import UIKit
Собственная concurrent queue с barrier сериализует запись относительно чтений. Но вызов completion внутри очереди создаёт риск reentrancy и deadlock при setValue из callback.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
import UIKit
protocol CacheProtocol { associatedtype Value func setValue(_ value: Value?, forKey key: String) func getValue(forKey key: String) -> Value? func fetchValue(forKey key: String, completion: @escaping (Value?) -> Void) }
final class Cache<T>: CacheProtocol { private var storage: [String: T] = [:] private let queue = DispatchQueue(label: "my.queue", attributes: .concurrent)
func setValue(_ value: T?, forKey key: String) {
queue.sync(flags: .barrier) {
storage[key] = value
}
}
func getValue(forKey key: String) -> T? {
queue.sync {
return storage[key]
}
}
func fetchValue(forKey key: String, completion: @escaping (T?) -> Void) {
queue.async {
let value = self.storage[key]
completion(value)
}
}
}
Ответ
Собственная concurrent queue с barrier сериализует запись относительно чтений. Но вызов completion внутри очереди создаёт риск reentrancy и deadlock при setValue из callback.
getValue использует sync и возвращает результат после чтения; setValue с barrier ждёт предыдущие блоки и выполняется эксклюзивно. Все обращения к storage должны проходить через эту же очередь.
fetchValue вызывает completion на внутренней очереди. Если callback синхронно вызовет setValue, barrier будет ждать завершения самого callback. Снимите значение внутри очереди, а callback вызовите на отдельно оговорённой очереди. Также копия T не обязательно изолирует ссылочное содержимое. Для строгой Swift concurrency потребуются корректные Sendable/изоляционные контракты; механическое @unchecked Sendable проблему не доказывает решённой.