Search code examples
swiftgenericslazy-evaluationlazy-initializationproperty-wrapper

Is Property Wrapper @Lazy variable thread safe?


We now have a new way to make a lazy variable. It is described in swift-evolution/proposals/0258-property-wrappers.md:

@propertyWrapper
enum Lazy<Value> {
    case uninitialized(() -> Value)
    case initialized(Value)

    init(wrappedValue: @autoclosure @escaping () -> Value) {
        self = .uninitialized(wrappedValue)
    }

    var wrappedValue: Value {
        mutating get {
            switch self {
            case .uninitialized(let initializer):
                let value = initializer()
                self = .initialized(value)
                return value
            case .initialized(let value):
                return value
            }
        }
        set {
            self = .initialized(newValue)
        }
    }
}

Is it a thread safe implementation? If not, how to reproduce non thread safe behavior?


Solution

  • I had the same question, so I tested it out by writing a small test that uses this property wrapper on a property in a class, and then try to print out the value (time since epoch) async (10000 times) and it blew up with a SIGABRT on the 'return value' line inside the getter of wrappedValue.

    I also tried just 'getting' the value without printing, and I had the same issue.

    So I would have to say: no, it is not thread safe.

    EDIT: I'd like to add that I did the same test on https://www.onswiftwings.com/posts/atomic-property-wrapper/ and that one is indeed thread safe, so you could use that as a base to make your own 'Lazy' that is thread safe.