I have created a "lock" in Swift and an Atomic property wrapper that uses that lock, for my Swift classes as Swift lacks ObjC's atomic
property attribute.
When I run my tests with thread sanitizer enabled, It always captures a data race on a property that uses my Atomic property wrapper.
The only thing that worked was changing the declaration of the property wrapper to be a class instead of a struct and the main question here is: why it works!
I have added print
s at the property wrapper and lock init
s to track the number of objects created, it was the same with struct/class, tried reproducing the issue in another project, didn't work too. But I will add the files the resembles the problem and let me know any guesses of why it works.
Lock
public class SwiftLock {
init() { }
public func sync<R>(execute: () throws -> R) rethrows -> R {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
return try execute()
}
}
Atomic property wrapper
@propertyWrapper struct Atomic<Value> {
let lock: SwiftLock
var value: Value
init(wrappedValue: Value, lock: SwiftLock=SwiftLock()) {
self.value = wrappedValue
self.lock = lock
}
var wrappedValue: Value {
get {
lock.sync { value }
}
set {
lock.sync { value = newValue }
}
}
}
Model (the data race should happen on the publicVariable2
property here)
class Model {
@Atomic var publicVariable: TimeInterval = 0
@Atomic var publicVariable2: TimeInterval = 0
var sessionDuration: TimeInterval {
min(0, publicVariable - publicVariable2)
}
}
Update 1: Full Xcode project: https://drive.google.com/file/d/1IfAsOdHKOqfuOp-pSlP75FLF32iVraru/view?usp=sharing
self
in the lock refers to theSwiftLock
notAtomic
– Logographyobjc_sync_enter
in that same discussion as the latter is old and very low-level – HenceforwardGCD
didn't work for me because a) I need the lock to be recursive b) synchronous, for those 2 reasons I'm doomed to a deadlock or a failed lock (concurrent execution). However, I have tried replacingSwiftLock
implementation to useNSRecursiveLock
(check update 2) and the issue persists and still fixed by changing the Atomic property wrapper declaration fromstruct to
class`. – Logography