Безопасность резьбы Swift COW

Я смотрю, как копирование на запись работает в Swift. И немного смущен isKnownUniquelyReferenced документация Особенно по этому разделу:

Если к экземпляру, переданному как объект, одновременно обращаются несколько потоков, эта функция может все еще возвращать true. Следовательно, вы должны вызывать эту функцию только из мутирующих методов с соответствующей синхронизацией потоков. Это гарантирует, что isKnownUniquelyReferenced(_:) возвращает true, только когда действительно один метод доступа или когда есть условие гонки, которое уже является неопределенным поведением.

Так что представьте себе случай.

  1. У нас есть структура COW без синхронизации внутри.
  2. Класс, который владеет экземпляром этой структуры и защищает его замками
  3. Геттер для этой структуры
  4. И хотите вернуть копию из этого потока получателя безопасно

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

Неправильная версия показывает гонку, о которой говорят документы.

Другие вопросы по тегам