Безопасность резьбы Swift COW
Я смотрю, как копирование на запись работает в Swift. И немного смущен isKnownUniquelyReferenced
документация Особенно по этому разделу:
Если к экземпляру, переданному как объект, одновременно обращаются несколько потоков, эта функция может все еще возвращать true. Следовательно, вы должны вызывать эту функцию только из мутирующих методов с соответствующей синхронизацией потоков. Это гарантирует, что isKnownUniquelyReferenced(_:) возвращает true, только когда действительно один метод доступа или когда есть условие гонки, которое уже является неопределенным поведением.
Так что представьте себе случай.
- У нас есть структура COW без синхронизации внутри.
- Класс, который владеет экземпляром этой структуры и защищает его замками
- Геттер для этой структуры
- И хотите вернуть копию из этого потока получателя безопасно
import Foundation
class StorageBuffer {
var field: Int = 1
init(_ field: Int) {
self.field = field
}
func copy() -> StorageBuffer {
return StorageBuffer(field)
}
}
struct Storage {
private var _buffer = StorageBuffer(1)
var field: Int {
get {
return _buffer.field
}
set {
if !isKnownUniquelyReferenced(&_buffer) {
_buffer = _buffer.copy()
}
_buffer.field = newValue
}
}
}
class StorageAware {
private var _storage = Storage()
private let _storageGuard = NSLock()
var storage: Storage {
_storageGuard.lock()
defer {
_storageGuard.unlock()
}
return _storage
}
}
Поскольку настоящее копирование произойдет позже. Достаточно ли синхронизировать геттер? Нужно ли это или структурирует потокобезопасность сами в этом случае? Есть какой-нибудь полный документ о безопасности быстрой резьбы где-нибудь?
0 ответов
Структуры CoW так же потокобезопасны, как и Int
, т.е. они не атомарны. Так же, как вам нужно заблокировать одновременный доступ при изменении Int, например
lock.lock()
var myInt = _storedInt // lock required, not an atomic op
lock.unlock()
myInt += 1
вы должны сделать то же самое при копировании структуры CoW (вы не показываете,_storage
сам когда-либо модифицируется, хотя отмечен как var
, так что это предполагает, что вам нужно заблокировать доступ на запись к _storage
).
Однако, когда вы это сделали isKnownUniquelyReferenced
гарантирует возврат истины, если ссылка уникальна.
Неправильно:
var myStorage = _storage // this can race
myStorage.value = 10
Правильно:
lock()
var myStorage = _storage // properly locked, can't race
unlock()
myStorage.value = 10 // this is safe now
Неправильная версия показывает гонку, о которой говорят документы.