Do I need some kind of explicit synchronization in this case?
class A {
let val: Int;
init(_ newVal: Int) {
val = newVal
}
}
public class B {
var a: A? = nil
public func setA() { a = A(0) }
public func hasA() -> Bool { return a != nil }
}
There is also another method in class B:
public func resetA() {
guard hasA() else { return }
a = A(1)
}
setA() and resetA() may be called from any thread, in any order.
I understand that there may be a race condition, that if concurrently one thread calls setA() and another thread calls resetA(), the result is not determined: val will either be 0, or 1, but I don't care: at any rate, hasA() will return true, won't it?
Does the answer change if A is a struct instead of class?
In short, no, property accessors are not atomic. See WWDC 2016 video Concurrent Programming With GCD in Swift 3, which talks about the absence of atomic/synchronization native in the language. (This is a GCD talk, so when they subsequently dive into synchronization methods, they focus on GCD methods, but any synchronization method is fine.) Apple uses a variety of different synchronization methods in their own code. E.g. in
ThreadSafeArrayStorethey use they useNSLock).If synchronizing with locks, I might suggest an extension like the following:
Apple uses this pattern in their own code, though they happen to call it
withLockrather thansynchronized. But the pattern is the same.Then you can do:
Or perhaps
I confess to some uneasiness in exposing
hasA, because it practically invites the application developer to write like:That is fine in terms of preventing simultaneous access to memory, but it introduced a logical race if two threads are doing it at the same time, where both happen to pass the
!hasAtest, and they both replace the value, the last one wins.Instead, I might write a method to do this for us:
That way you can do:
That is thread safe, because we are letting the caller wrap all the logical tasks (checking to see if
aisniland, if so, the initialization ofa) all in one single synchronized step. It is a nice generalized solution to the problem. And it prevents logic races.Now the above example is so abstract that it is hard to follow. So let's consider a practical example, a variation on Apple’s
ThreadSafeArrayStore:Here we have a synchronized array, where we define an interface to interact with the underlying array in a thread-safe manner.
Or, if you want an even more trivial example, consider an thread-safe object to keep track of what the tallest item was. We would not have a
hasValueboolean, but rather we would incorporate that right into our synchronizedupdateIfTallermethod:Just a few examples. Hopefully it illustrates the idea.