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
ThreadSafeArrayStore
they 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
withLock
rather 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
!hasA
test, 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
a
isnil
and, 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
hasValue
boolean, but rather we would incorporate that right into our synchronizedupdateIfTaller
method:Just a few examples. Hopefully it illustrates the idea.