swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.57k stars 10.36k forks source link

[SR-12544] Property wrapper exclusive access issue #54988

Open 05109ee7-7cd9-4cd4-92d0-698e676fc6af opened 4 years ago

05109ee7-7cd9-4cd4-92d0-698e676fc6af commented 4 years ago
Previous ID SR-12544
Radar rdar://problem/62201658
Original Reporter @an0
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: dd011de75bdee80984c8950b71db9f7c

Issue Description:

At the end of wrapper setter, I post a notification. The observer receives the notification, naturally starts using the new value. It causes exclusive access violation.

The same patten works perfectly for normal properties.

import Cocoa

class Prefs: NSObject {

    static let fooDidChage = Notification.Name("fooDidChange")
    static let barDidChage = Notification.Name("barDidChange")

    private static var defaults: [String: Any] = [:]

    @propertyWrapper
    struct UserDefaults<T> {

        let key: String
        let defaultValue: T

        init(key: String, defaultValue: T) {
            self.key = key
            self.defaultValue = defaultValue
        }

        var wrappedValue: T {
            get {
                defaults[key] as? T ?? defaultValue
            }
            set {
                defaults[key] = newValue
                NotificationCenter.default.post(name: barDidChage, object: Prefs.self)
            }
        }

    }

    static var foo: Int {
        get {
            defaults["foo"] as? Int ?? 0
        }

        set {
            defaults["foo"] = newValue
            NotificationCenter.default.post(name: fooDidChage, object: self)
        }
    }

    @UserDefaults(key: "bar", defaultValue: 0) static var bar: Int

}

NotificationCenter.default.addObserver(forName: Prefs.fooDidChage, object: Prefs.self, queue: .main) { (notif) in
    print(notif)
    print(Prefs.foo)
}

print(Prefs.foo)
Prefs.foo = 1

// setting->observing->getting foo works. The same should also work for bar.
NotificationCenter.default.addObserver(forName: Prefs.barDidChage, object: Prefs.self, queue: .main) { (notif) in
    print(notif)
    print(Prefs.bar)
}

print(Prefs.bar)
Prefs.bar = 1 // Simultaneous accesses to 0x1100d9988, but modification requires exclusive access.
beccadax commented 4 years ago

@swift-ci create